1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <b dot purcell at adbglobal dot com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <stdlib.h> /* malloc(), free() */
32 #ifdef HAVE_LINUX_LIMITS_H
33 # include <linux/limits.h>
39 #include <vlc_block.h>
40 #include <vlc_filter.h>
41 #include <vlc_stream.h>
51 #include FT_FREETYPE_H
53 #define FT_FLOOR(X) ((X & -64) >> 6)
54 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
55 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
59 #define FC_DEFAULT_FONT "Lucida Grande"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
67 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
68 #define FC_DEFAULT_FONT "Serif Bold"
71 #if defined(HAVE_FRIBIDI)
72 #include <fribidi/fribidi.h>
75 #ifdef HAVE_FONTCONFIG
76 #include <fontconfig/fontconfig.h>
79 typedef struct line_desc_t line_desc_t;
81 /*****************************************************************************
83 *****************************************************************************/
84 static int Create ( vlc_object_t * );
85 static void Destroy( vlc_object_t * );
87 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
88 static int RenderText( filter_t *, subpicture_region_t *,
89 subpicture_region_t * );
90 #ifdef HAVE_FONTCONFIG
91 static int RenderHtml( filter_t *, subpicture_region_t *,
92 subpicture_region_t * );
93 static char *FontConfig_Select( FcConfig *, const char *,
94 vlc_bool_t, vlc_bool_t, int * );
96 static line_desc_t *NewLine( byte_t * );
98 static int SetFontSize( filter_t *, int );
100 /*****************************************************************************
102 *****************************************************************************/
103 #define FONT_TEXT N_("Font")
104 #define FONT_LONGTEXT N_("Filename for the font you want to use")
105 #define FONTSIZE_TEXT N_("Font size in pixels")
106 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
107 "that will be rendered on the video. " \
108 "If set to something different than 0 this option will override the " \
109 "relative font size." )
110 #define OPACITY_TEXT N_("Opacity")
111 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
112 "text that will be rendered on the video. 0 = transparent, " \
113 "255 = totally opaque. " )
114 #define COLOR_TEXT N_("Text default color")
115 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
116 "the video. This must be an hexadecimal (like HTML colors). The first two "\
117 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
118 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
119 #define FONTSIZER_TEXT N_("Relative font size")
120 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
121 "fonts that will be rendered on the video. If absolute font size is set, "\
122 "relative size will be overriden." )
124 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
125 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
126 N_("Large"), N_("Larger") };
127 #define YUVP_TEXT N_("Use YUVP renderer")
128 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
129 "This option is only needed if you want to encode into DVB subtitles" )
130 #define EFFECT_TEXT N_("Font Effect")
131 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
132 "text to improve its readability." )
134 #define EFFECT_BACKGROUND 1
135 #define EFFECT_OUTLINE 2
136 #define EFFECT_OUTLINE_FAT 3
138 static int pi_effects[] = { 1, 2, 3 };
139 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
141 static int pi_color_values[] = {
142 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
143 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
144 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
146 static const char *ppsz_color_descriptions[] = {
147 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
148 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
149 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
152 set_shortname( _("Text renderer"));
153 set_description( _("Freetype2 font renderer") );
154 set_category( CAT_VIDEO );
155 set_subcategory( SUBCAT_VIDEO_SUBPIC );
157 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
160 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
161 FONTSIZE_LONGTEXT, VLC_TRUE );
163 /* opacity valid on 0..255, with default 255 = fully opaque */
164 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
165 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
167 /* hook to the color values list, with default 0x00ffffff = white */
168 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
169 COLOR_LONGTEXT, VLC_FALSE );
170 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
172 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
173 FONTSIZER_LONGTEXT, VLC_FALSE );
174 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
175 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
176 EFFECT_LONGTEXT, VLC_FALSE );
177 change_integer_list( pi_effects, ppsz_effects_text, 0 );
179 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
180 YUVP_LONGTEXT, VLC_TRUE );
181 set_capability( "text renderer", 100 );
182 add_shortcut( "text" );
183 set_callbacks( Create, Destroy );
188 /** NULL-terminated list of glyphs making the string */
189 FT_BitmapGlyph *pp_glyphs;
190 /** list of relative positions for the glyphs */
191 FT_Vector *p_glyph_pos;
192 /** list of RGB information for styled text
193 * -- if the rendering mode supports it (RenderYUVA) and
194 * b_new_color_mode is set, then it becomes possible to
195 * have multicoloured text within the subtitles. */
197 vlc_bool_t b_new_color_mode;
198 /** underline information -- only supplied if text should be underlined */
199 uint16_t *pi_underline_offset;
200 uint16_t *pi_underline_thickness;
204 int i_red, i_green, i_blue;
210 typedef struct font_stack_t font_stack_t;
218 font_stack_t *p_next;
221 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
222 static void FreeLines( line_desc_t * );
223 static void FreeLine( line_desc_t * );
225 /*****************************************************************************
226 * filter_sys_t: freetype local data
227 *****************************************************************************
228 * This structure is part of the video output thread descriptor.
229 * It describes the freetype specific properties of an output thread.
230 *****************************************************************************/
233 FT_Library p_library; /* handle to library */
234 FT_Face p_face; /* handle to face object */
235 vlc_bool_t i_use_kerning;
236 uint8_t i_font_opacity;
241 int i_default_font_size;
242 int i_display_height;
243 #ifdef HAVE_FONTCONFIG
244 FcConfig *p_fontconfig;
248 /*****************************************************************************
249 * Create: allocates osd-text video thread output method
250 *****************************************************************************
251 * This function allocates and initializes a Clone vout method.
252 *****************************************************************************/
253 static int Create( vlc_object_t *p_this )
255 filter_t *p_filter = (filter_t *)p_this;
257 char *psz_fontfile = NULL;
261 /* Allocate structure */
262 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
265 msg_Err( p_filter, "out of memory" );
269 p_sys->p_library = 0;
270 p_sys->i_font_size = 0;
271 p_sys->i_display_height = 0;
273 var_Create( p_filter, "freetype-font",
274 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
275 var_Create( p_filter, "freetype-fontsize",
276 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
277 var_Create( p_filter, "freetype-rel-fontsize",
278 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
279 var_Create( p_filter, "freetype-opacity",
280 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
281 var_Create( p_filter, "freetype-effect",
282 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
283 var_Get( p_filter, "freetype-opacity", &val );
284 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
285 var_Create( p_filter, "freetype-color",
286 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
287 var_Get( p_filter, "freetype-color", &val );
288 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
289 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
291 /* Look what method was requested */
292 var_Get( p_filter, "freetype-font", &val );
293 psz_fontfile = val.psz_string;
294 if( !psz_fontfile || !*psz_fontfile )
296 if( psz_fontfile ) free( psz_fontfile );
297 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
300 msg_Err( p_filter, "out of memory" );
304 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
305 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
306 #elif defined(__APPLE__)
307 strcpy( psz_fontfile, DEFAULT_FONT );
309 msg_Err( p_filter, "user didn't specify a font" );
314 #ifdef HAVE_FONTCONFIG
316 p_sys->p_fontconfig = FcConfigGetCurrent();
318 p_sys->p_fontconfig = NULL;
320 i_error = FT_Init_FreeType( &p_sys->p_library );
323 msg_Err( p_filter, "couldn't initialize freetype" );
327 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
329 if( i_error == FT_Err_Unknown_File_Format )
331 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
336 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
340 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
343 msg_Err( p_filter, "font has no unicode translation table" );
347 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
349 var_Get( p_filter, "freetype-fontsize", &val );
350 p_sys->i_default_font_size = val.i_int;
351 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
353 if( psz_fontfile ) free( psz_fontfile );
354 p_filter->pf_render_text = RenderText;
355 #ifdef HAVE_FONTCONFIG
356 p_filter->pf_render_html = RenderHtml;
358 p_filter->pf_render_html = NULL;
363 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
364 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
365 if( psz_fontfile ) free( psz_fontfile );
370 /*****************************************************************************
371 * Destroy: destroy Clone video thread output method
372 *****************************************************************************
373 * Clean up all data and library connections
374 *****************************************************************************/
375 static void Destroy( vlc_object_t *p_this )
377 filter_t *p_filter = (filter_t *)p_this;
378 filter_sys_t *p_sys = p_filter->p_sys;
380 #ifdef HAVE_FONTCONFIG
381 FcConfigDestroy( p_sys->p_fontconfig );
382 p_sys->p_fontconfig = NULL;
383 /* FcFini asserts calling the subfunction FcCacheFini()
384 * even if no other library functions have been made since FcInit(),
385 * so don't call it. */
387 FT_Done_Face( p_sys->p_face );
388 FT_Done_FreeType( p_sys->p_library );
392 /*****************************************************************************
393 * Render: place string in picture
394 *****************************************************************************
395 * This function merges the previously rendered freetype glyphs into a picture
396 *****************************************************************************/
397 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
398 line_desc_t *p_line, int i_width, int i_height )
400 static uint8_t pi_gamma[16] =
401 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
402 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
406 int i, x, y, i_pitch;
407 uint8_t i_y; /* YUV values, derived from incoming RGB */
409 subpicture_region_t *p_region_tmp;
411 /* Create a new subpicture region */
412 memset( &fmt, 0, sizeof(video_format_t) );
413 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
415 fmt.i_width = fmt.i_visible_width = i_width + 4;
416 fmt.i_height = fmt.i_visible_height = i_height + 4;
417 if( p_region->fmt.i_visible_width > 0 )
418 fmt.i_visible_width = p_region->fmt.i_visible_width;
419 if( p_region->fmt.i_visible_height > 0 )
420 fmt.i_visible_height = p_region->fmt.i_visible_height;
421 fmt.i_x_offset = fmt.i_y_offset = 0;
422 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
425 msg_Err( p_filter, "cannot allocate SPU region" );
429 p_region->fmt = p_region_tmp->fmt;
430 p_region->picture = p_region_tmp->picture;
431 free( p_region_tmp );
433 /* Calculate text color components */
434 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
435 25 * p_line->i_blue + 128) >> 8) + 16;
436 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
437 112 * p_line->i_blue + 128) >> 8) + 128;
438 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
439 18 * p_line->i_blue + 128) >> 8) + 128;
442 fmt.p_palette->i_entries = 16;
443 for( i = 0; i < 8; i++ )
445 fmt.p_palette->palette[i][0] = 0;
446 fmt.p_palette->palette[i][1] = 0x80;
447 fmt.p_palette->palette[i][2] = 0x80;
448 fmt.p_palette->palette[i][3] = pi_gamma[i];
449 fmt.p_palette->palette[i][3] =
450 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
452 for( i = 8; i < fmt.p_palette->i_entries; i++ )
454 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
455 fmt.p_palette->palette[i][1] = i_u;
456 fmt.p_palette->palette[i][2] = i_v;
457 fmt.p_palette->palette[i][3] = pi_gamma[i];
458 fmt.p_palette->palette[i][3] =
459 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
462 p_dst = p_region->picture.Y_PIXELS;
463 i_pitch = p_region->picture.Y_PITCH;
465 /* Initialize the region pixels */
466 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
468 for( ; p_line != NULL; p_line = p_line->p_next )
470 int i_glyph_tmax = 0;
471 int i_bitmap_offset, i_offset, i_align_offset = 0;
472 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
474 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
475 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
478 if( p_line->i_width < i_width )
480 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
481 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
483 i_align_offset = i_width - p_line->i_width;
485 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
486 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
488 i_align_offset = ( i_width - p_line->i_width ) / 2;
492 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
494 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
496 i_offset = ( p_line->p_glyph_pos[ i ].y +
497 i_glyph_tmax - p_glyph->top + 2 ) *
498 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
501 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
503 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
505 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
507 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
514 /* Outlining (find something better than nearest neighbour filtering ?) */
517 uint8_t *p_dst = p_region->picture.Y_PIXELS;
518 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
519 uint8_t left, current;
521 for( y = 1; y < (int)fmt.i_height - 1; y++ )
523 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
524 p_dst += p_region->picture.Y_PITCH;
527 for( x = 1; x < (int)fmt.i_width - 1; x++ )
530 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
531 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;
535 memset( p_top, 0, fmt.i_width );
541 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
542 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
543 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
544 int i_glyph_tmax, int i_align_offset,
545 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
546 subpicture_region_t *p_region)
550 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
552 p_dst_y = p_region->picture.Y_PIXELS;
553 p_dst_u = p_region->picture.U_PIXELS;
554 p_dst_v = p_region->picture.V_PIXELS;
555 p_dst_a = p_region->picture.A_PIXELS;
556 i_pitch = p_region->picture.A_PITCH;
558 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
559 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
561 for( y = 0; y < i_line_thickness; y++ )
563 int i_extra = p_this_glyph->bitmap.width;
567 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
568 (p_this_glyph_pos->x + p_this_glyph->left);
570 for( x = 0; x < i_extra; x++ )
572 vlc_bool_t b_ok = VLC_TRUE;
574 /* break the underline around the tails of any glyphs which cross it */
575 for( z = x - i_line_thickness;
576 z < x + i_line_thickness && b_ok;
579 if( p_next_glyph && ( z >= i_extra ) )
581 int i_row = i_line_offset + p_next_glyph->top + y;
583 if( ( p_next_glyph->bitmap.rows > i_row ) &&
584 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
589 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
591 int i_row = i_line_offset + p_this_glyph->top + y;
593 if( ( p_this_glyph->bitmap.rows > i_row ) &&
594 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
603 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
604 p_dst_u[i_offset+x] = i_u;
605 p_dst_v[i_offset+x] = i_v;
606 p_dst_a[i_offset+x] = 255;
613 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
615 uint8_t *p_dst = p_region->picture.A_PIXELS;
616 int i_pitch = p_region->picture.A_PITCH;
619 for( ; p_line != NULL; p_line = p_line->p_next )
621 int i_glyph_tmax=0, i = 0;
622 int i_bitmap_offset, i_offset, i_align_offset = 0;
623 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
625 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
626 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
629 if( p_line->i_width < i_width )
631 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
632 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
634 i_align_offset = i_width - p_line->i_width;
636 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
637 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
639 i_align_offset = ( i_width - p_line->i_width ) / 2;
643 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
645 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
647 i_offset = ( p_line->p_glyph_pos[ i ].y +
648 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
649 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
650 i_align_offset +xoffset;
652 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
654 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
656 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
657 if( p_dst[i_offset+x] <
658 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
660 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
669 /*****************************************************************************
670 * Render: place string in picture
671 *****************************************************************************
672 * This function merges the previously rendered freetype glyphs into a picture
673 *****************************************************************************/
674 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
675 line_desc_t *p_line, int i_width, int i_height )
677 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
679 int i, x, y, i_pitch, i_alpha;
680 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
681 subpicture_region_t *p_region_tmp;
683 if( i_width == 0 || i_height == 0 )
686 /* Create a new subpicture region */
687 memset( &fmt, 0, sizeof(video_format_t) );
688 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
690 fmt.i_width = fmt.i_visible_width = i_width + 6;
691 fmt.i_height = fmt.i_visible_height = i_height + 6;
692 if( p_region->fmt.i_visible_width > 0 )
693 fmt.i_visible_width = p_region->fmt.i_visible_width;
694 if( p_region->fmt.i_visible_height > 0 )
695 fmt.i_visible_height = p_region->fmt.i_visible_height;
696 fmt.i_x_offset = fmt.i_y_offset = 0;
697 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
700 msg_Err( p_filter, "cannot allocate SPU region" );
704 p_region->fmt = p_region_tmp->fmt;
705 p_region->picture = p_region_tmp->picture;
706 free( p_region_tmp );
708 /* Calculate text color components */
709 i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red + 4130 * p_line->i_green +
710 802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
711 i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red + -2384 * p_line->i_green +
712 3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
713 i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
714 -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
715 i_alpha = p_line->i_alpha;
717 p_dst_y = p_region->picture.Y_PIXELS;
718 p_dst_u = p_region->picture.U_PIXELS;
719 p_dst_v = p_region->picture.V_PIXELS;
720 p_dst_a = p_region->picture.A_PIXELS;
721 i_pitch = p_region->picture.A_PITCH;
723 /* Initialize the region pixels */
724 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
726 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
727 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
728 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
729 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
733 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
734 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
735 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
736 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
738 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
739 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
741 DrawBlack( p_line, i_width, p_region, 0, 0);
742 DrawBlack( p_line, i_width, p_region, -1, 0);
743 DrawBlack( p_line, i_width, p_region, 0, -1);
744 DrawBlack( p_line, i_width, p_region, 1, 0);
745 DrawBlack( p_line, i_width, p_region, 0, 1);
748 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
750 DrawBlack( p_line, i_width, p_region, -1, -1);
751 DrawBlack( p_line, i_width, p_region, -1, 1);
752 DrawBlack( p_line, i_width, p_region, 1, -1);
753 DrawBlack( p_line, i_width, p_region, 1, 1);
755 DrawBlack( p_line, i_width, p_region, -2, 0);
756 DrawBlack( p_line, i_width, p_region, 0, -2);
757 DrawBlack( p_line, i_width, p_region, 2, 0);
758 DrawBlack( p_line, i_width, p_region, 0, 2);
760 DrawBlack( p_line, i_width, p_region, -2, -2);
761 DrawBlack( p_line, i_width, p_region, -2, 2);
762 DrawBlack( p_line, i_width, p_region, 2, -2);
763 DrawBlack( p_line, i_width, p_region, 2, 2);
765 DrawBlack( p_line, i_width, p_region, -3, 0);
766 DrawBlack( p_line, i_width, p_region, 0, -3);
767 DrawBlack( p_line, i_width, p_region, 3, 0);
768 DrawBlack( p_line, i_width, p_region, 0, 3);
771 for( ; p_line != NULL; p_line = p_line->p_next )
773 int i_glyph_tmax = 0;
774 int i_bitmap_offset, i_offset, i_align_offset = 0;
775 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
777 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
778 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
781 if( p_line->i_width < i_width )
783 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
784 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
786 i_align_offset = i_width - p_line->i_width;
788 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
789 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
791 i_align_offset = ( i_width - p_line->i_width ) / 2;
795 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
797 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
799 i_offset = ( p_line->p_glyph_pos[ i ].y +
800 i_glyph_tmax - p_glyph->top + 3 ) *
801 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
804 if( p_line->b_new_color_mode )
806 /* Every glyph can (and in fact must) have its own color */
807 int i_red = ( p_line->p_rgb[ i ] & 0x00ff0000 ) >> 16;
808 int i_green = ( p_line->p_rgb[ i ] & 0x0000ff00 ) >> 8;
809 int i_blue = ( p_line->p_rgb[ i ] & 0x000000ff );
811 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
812 802 * i_blue + 4096 + 131072 ) >> 13, 235);
813 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
814 3598 * i_blue + 4096 + 1048576) >> 13, 240);
815 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
816 -585 * i_blue + 4096 + 1048576) >> 13, 240);
819 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
821 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
823 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
825 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
826 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
828 p_dst_u[i_offset+x] = i_u;
829 p_dst_v[i_offset+x] = i_v;
831 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
832 p_dst_a[i_offset+x] = 0xff;
838 if( p_line->pi_underline_thickness[ i ] )
840 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
841 p_line->pi_underline_offset[ i ],
842 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
843 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
844 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
845 i_glyph_tmax, i_align_offset,
846 i_y, i_u, i_v, i_alpha,
852 /* Apply the alpha setting */
853 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
854 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
860 * This function renders a text subpicture region into another one.
861 * It also calculates the size needed for this string, and renders the
862 * needed glyphs into memory. It is used as pf_add_string callback in
863 * the vout method by this module
865 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
866 subpicture_region_t *p_region_in )
868 filter_sys_t *p_sys = p_filter->p_sys;
869 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
870 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
871 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
874 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
875 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
883 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
884 psz_string = p_region_in->psz_text;
885 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
887 if( p_region_in->p_style )
889 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
890 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
891 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
895 i_font_color = p_sys->i_font_color;
896 i_font_alpha = 255 - p_sys->i_font_opacity;
897 i_font_size = p_sys->i_default_font_size;
900 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
901 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
902 SetFontSize( p_filter, i_font_size );
904 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
905 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
906 i_blue = i_font_color & 0x000000FF;
908 result.x = result.y = 0;
909 line.xMin = line.xMax = line.yMin = line.yMax = 0;
911 psz_unicode = psz_unicode_orig =
912 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
913 if( psz_unicode == NULL )
915 msg_Err( p_filter, "out of memory" );
918 #if defined(WORDS_BIGENDIAN)
919 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
921 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
923 if( iconv_handle == (vlc_iconv_t)-1 )
925 msg_Warn( p_filter, "unable to do conversion" );
931 const char *p_in_buffer = psz_string;
932 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
933 i_in_bytes = strlen( psz_string );
934 i_out_bytes = i_in_bytes * sizeof( uint32_t );
935 i_out_bytes_left = i_out_bytes;
936 p_out_buffer = (char *)psz_unicode;
937 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
938 &p_out_buffer, &i_out_bytes_left );
940 vlc_iconv_close( iconv_handle );
944 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
945 "bytes left %d", strerror(errno), (int)i_in_bytes );
948 *(uint32_t*)p_out_buffer = 0;
949 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
952 #if defined(HAVE_FRIBIDI)
954 uint32_t *p_fribidi_string;
955 int start_pos, pos = 0;
957 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
958 if( !p_fribidi_string )
960 msg_Err( p_filter, "out of memory" );
964 /* Do bidi conversion line-by-line */
965 while(pos < i_string_length)
967 while(pos < i_string_length) {
968 i_char = psz_unicode[pos];
969 if (i_char != '\r' && i_char != '\n')
971 p_fribidi_string[pos] = i_char;
975 while(pos < i_string_length) {
976 i_char = psz_unicode[pos];
977 if (i_char == '\r' || i_char == '\n')
983 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
984 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
985 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
989 free( psz_unicode_orig );
990 psz_unicode = psz_unicode_orig = p_fribidi_string;
991 p_fribidi_string[ i_string_length ] = 0;
995 /* Calculate relative glyph positions and a bounding box for the
997 if( !(p_line = NewLine( (byte_t *)psz_string )) )
999 msg_Err( p_filter, "out of memory" );
1003 i_pen_x = i_pen_y = 0;
1005 psz_line_start = psz_unicode;
1007 #define face p_sys->p_face
1008 #define glyph face->glyph
1010 while( *psz_unicode )
1012 i_char = *psz_unicode++;
1013 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1018 if( i_char == '\n' )
1020 psz_line_start = psz_unicode;
1021 if( !(p_next = NewLine( (byte_t *)psz_string )) )
1023 msg_Err( p_filter, "out of memory" );
1026 p_line->p_next = p_next;
1027 p_line->i_width = line.xMax;
1028 p_line->i_height = face->size->metrics.height >> 6;
1029 p_line->pp_glyphs[ i ] = NULL;
1030 p_line->i_alpha = i_font_alpha;
1031 p_line->i_red = i_red;
1032 p_line->i_green = i_green;
1033 p_line->i_blue = i_blue;
1036 result.x = __MAX( result.x, line.xMax );
1037 result.y += face->size->metrics.height >> 6;
1040 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1041 i_pen_y += face->size->metrics.height >> 6;
1043 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1048 i_glyph_index = FT_Get_Char_Index( face, i_char );
1049 if( p_sys->i_use_kerning && i_glyph_index
1053 FT_Get_Kerning( face, i_previous, i_glyph_index,
1054 ft_kerning_default, &delta );
1055 i_pen_x += delta.x >> 6;
1058 p_line->p_glyph_pos[ i ].x = i_pen_x;
1059 p_line->p_glyph_pos[ i ].y = i_pen_y;
1060 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1063 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1067 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1070 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1074 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1075 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1078 FT_Done_Glyph( tmp_glyph );
1081 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1084 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1085 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1086 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1088 p_line->pp_glyphs[ i ] = NULL;
1090 p_line = NewLine( (byte_t *)psz_string );
1091 if( p_prev ) p_prev->p_next = p_line;
1092 else p_lines = p_line;
1094 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1098 if( psz_unicode == psz_line_start )
1100 msg_Warn( p_filter, "unbreakable string" );
1105 *psz_unicode = '\n';
1107 psz_unicode = psz_line_start;
1110 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1113 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1114 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1116 i_previous = i_glyph_index;
1117 i_pen_x += glyph->advance.x >> 6;
1121 p_line->i_width = line.xMax;
1122 p_line->i_height = face->size->metrics.height >> 6;
1123 p_line->pp_glyphs[ i ] = NULL;
1124 p_line->i_alpha = i_font_alpha;
1125 p_line->i_red = i_red;
1126 p_line->i_green = i_green;
1127 p_line->i_blue = i_blue;
1128 result.x = __MAX( result.x, line.xMax );
1129 result.y += line.yMax - line.yMin;
1134 p_region_out->i_x = p_region_in->i_x;
1135 p_region_out->i_y = p_region_in->i_y;
1137 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1138 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1140 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1142 if( psz_unicode_orig ) free( psz_unicode_orig );
1143 FreeLines( p_lines );
1147 if( psz_unicode_orig ) free( psz_unicode_orig );
1148 FreeLines( p_lines );
1149 return VLC_EGENERIC;
1152 #ifdef HAVE_FONTCONFIG
1153 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1154 int i_color, int i_alpha )
1156 font_stack_t *p_new;
1159 return VLC_EGENERIC;
1161 p_new = malloc( sizeof( font_stack_t ) );
1165 p_new->p_next = NULL;
1168 p_new->psz_name = strdup( psz_name );
1170 p_new->psz_name = NULL;
1172 p_new->i_size = i_size;
1173 p_new->i_color = i_color;
1174 p_new->i_alpha = i_alpha;
1182 font_stack_t *p_last;
1184 for( p_last = *p_font;
1186 p_last = p_last->p_next )
1189 p_last->p_next = p_new;
1194 static int PopFont( font_stack_t **p_font )
1196 font_stack_t *p_last, *p_next_to_last;
1198 if( !p_font || !*p_font )
1199 return VLC_EGENERIC;
1201 p_next_to_last = NULL;
1202 for( p_last = *p_font;
1204 p_last = p_last->p_next )
1206 p_next_to_last = p_last;
1209 if( p_next_to_last )
1210 p_next_to_last->p_next = NULL;
1214 free( p_last->psz_name );
1220 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1221 int *i_color, int *i_alpha )
1223 font_stack_t *p_last;
1225 if( !p_font || !*p_font )
1226 return VLC_EGENERIC;
1228 for( p_last=*p_font;
1230 p_last=p_last->p_next )
1233 *psz_name = p_last->psz_name;
1234 *i_size = p_last->i_size;
1235 *i_color = p_last->i_color;
1236 *i_alpha = p_last->i_alpha;
1241 static uint32_t *IconvText( filter_t *p_filter, char *psz_string )
1243 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1244 uint32_t *psz_unicode;
1245 int i_string_length;
1248 malloc( ( strlen( psz_string ) + 1 ) * sizeof( uint32_t ) );
1249 if( psz_unicode == NULL )
1251 msg_Err( p_filter, "out of memory" );
1254 #if defined(WORDS_BIGENDIAN)
1255 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1257 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1259 if( iconv_handle == (vlc_iconv_t)-1 )
1261 msg_Warn( p_filter, "unable to do conversion" );
1262 free( psz_unicode );
1267 char *p_in_buffer, *p_out_buffer;
1268 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1269 i_in_bytes = strlen( psz_string );
1270 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1271 i_out_bytes_left = i_out_bytes;
1272 p_in_buffer = psz_string;
1273 p_out_buffer = (char *)psz_unicode;
1274 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1275 &p_out_buffer, &i_out_bytes_left );
1277 vlc_iconv_close( iconv_handle );
1281 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1282 "bytes left %d", strerror(errno), (int)i_in_bytes );
1283 free( psz_unicode );
1286 *(uint32_t*)p_out_buffer = 0;
1287 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1290 #if defined(HAVE_FRIBIDI)
1292 uint32_t *p_fribidi_string;
1294 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1295 if( !p_fribidi_string )
1297 msg_Err( p_filter, "out of memory" );
1298 free( psz_unicode );
1302 /* Do bidi conversion line-by-line */
1303 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1304 fribidi_log2vis((FriBidiChar*)psz_unicode, i_string_length,
1305 &base_dir, (FriBidiChar*)p_fribidi_string, 0, 0, 0);
1307 free( psz_unicode );
1308 psz_unicode = p_fribidi_string;
1309 p_fribidi_string[ i_string_length ] = 0;
1315 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1316 vlc_bool_t b_uline, line_desc_t *p_line, uint32_t *psz_unicode,
1317 int *pi_pen_x, int i_pen_y, int *pi_start,
1318 FT_Vector *p_result )
1325 int i_pen_x_start = *pi_pen_x;
1327 uint32_t *psz_unicode_start = psz_unicode;
1329 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1331 /* Account for part of line already in position */
1332 for( i=0; i<*pi_start; i++ )
1336 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ], ft_glyph_bbox_pixels, &glyph_size );
1338 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1339 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1340 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1341 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1346 while( *psz_unicode && ( *psz_unicode != 0xffff ) )
1352 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1353 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1357 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1358 ft_kerning_default, &delta );
1359 *pi_pen_x += delta.x >> 6;
1361 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1362 p_line->p_glyph_pos[ i ].y = i_pen_y;
1364 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1367 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned %d", i_error );
1368 p_line->pp_glyphs[ i ] = NULL;
1369 return VLC_EGENERIC;
1371 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1374 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned %d", i_error );
1375 p_line->pp_glyphs[ i ] = NULL;
1376 return VLC_EGENERIC;
1378 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1379 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1382 FT_Done_Glyph( tmp_glyph );
1387 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position, p_face->size->metrics.y_scale));
1388 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness, p_face->size->metrics.y_scale));
1390 p_line->pi_underline_offset[ i ] = ( aOffset < 0 ) ? -aOffset : aOffset;
1391 p_line->pi_underline_thickness[ i ] = ( aSize < 0 ) ? -aSize : aSize;
1393 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1394 p_line->p_rgb[ i ] = i_font_color & 0x00ffffff;
1396 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1397 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1398 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1400 while( --i > *pi_start )
1402 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1405 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1409 if( psz_unicode == psz_unicode_start )
1411 msg_Warn( p_filter, "unbreakable string" );
1412 p_line->pp_glyphs[ i ] = NULL;
1413 return VLC_EGENERIC;
1417 *psz_unicode = 0xffff;
1419 psz_unicode = psz_unicode_start;
1420 *pi_pen_x = i_pen_x_start;
1428 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1429 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1431 i_previous = i_glyph_index;
1432 *pi_pen_x += p_face->glyph->advance.x >> 6;
1435 p_line->i_width = line.xMax;
1436 p_line->i_height = __MAX( p_line->i_height, p_face->size->metrics.height >> 6 );
1437 p_line->pp_glyphs[ i ] = NULL;
1439 p_result->x = __MAX( p_result->x, line.xMax );
1440 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height, line.yMax - line.yMin ) );
1444 /* Get rid of any text processed - if necessary repositioning
1445 * at the start of a new line of text
1449 *psz_unicode_start = '\0';
1454 for( i=0; psz_unicode[ i ]; i++ )
1455 psz_unicode_start[ i ] = psz_unicode[ i ];
1456 psz_unicode_start[ i ] = '\0';
1462 static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader, char *psz_html, text_style_t *p_font_style, line_desc_t **p_lines, FT_Vector *p_result )
1464 filter_sys_t *p_sys = p_filter->p_sys;
1466 FT_Vector tmp_result;
1468 font_stack_t *p_fonts = NULL;
1469 vlc_bool_t b_italic = VLC_FALSE;
1470 vlc_bool_t b_bold = VLC_FALSE;
1471 vlc_bool_t b_uline = VLC_FALSE;
1473 line_desc_t *p_line = NULL;
1474 line_desc_t *p_prev = NULL;
1476 char *psz_node = NULL;
1482 int rv = VLC_SUCCESS;
1484 p_result->x = p_result->y = 0;
1485 tmp_result.x = tmp_result.y = 0;
1490 p_font_style->psz_fontname,
1491 p_font_style->i_font_size,
1492 p_font_style->i_font_color,
1493 p_font_style->i_font_alpha );
1495 if( p_font_style->i_style_flags & STYLE_BOLD )
1497 if( p_font_style->i_style_flags & STYLE_ITALIC )
1498 b_italic = VLC_TRUE;
1499 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1504 PushFont( &p_fonts, FC_DEFAULT_FONT, p_sys->i_font_size, 0xffffff, 0 );
1507 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) && ( rv == VLC_SUCCESS ) )
1509 switch ( xml_ReaderNodeType( p_xml_reader ) )
1511 case XML_READER_NONE:
1513 case XML_READER_ENDELEM:
1514 psz_node = xml_ReaderName( p_xml_reader );
1518 if( !strcasecmp( "font", psz_node ) )
1519 PopFont( &p_fonts );
1520 else if( !strcasecmp( "b", psz_node ) )
1522 else if( !strcasecmp( "i", psz_node ) )
1523 b_italic = VLC_FALSE;
1524 else if( !strcasecmp( "u", psz_node ) )
1525 b_uline = VLC_FALSE;
1530 case XML_READER_STARTELEM:
1531 psz_node = xml_ReaderName( p_xml_reader );
1534 if( !strcasecmp( "font", psz_node ) )
1536 char *psz_fontname = NULL;
1537 int i_font_color = 0xffffff;
1538 int i_font_alpha = 0;
1539 int i_font_size = 24;
1541 /* Default all attributes to the top font in the stack -- in case not
1542 * all attributes are specified in the sub-font
1544 if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ))
1546 psz_fontname = strdup( psz_fontname );
1549 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1551 char *psz_name = xml_ReaderName ( p_xml_reader );
1552 char *psz_value = xml_ReaderValue ( p_xml_reader );
1554 if( psz_name && psz_value )
1556 if( !strcasecmp( "face", psz_name ) )
1558 if( psz_fontname ) free( psz_fontname );
1559 psz_fontname = strdup( psz_value );
1561 else if( !strcasecmp( "size", psz_name ) )
1563 i_font_size = atoi( psz_value );
1565 else if( !strcasecmp( "color", psz_name ) &&
1566 ( psz_value[0] == '#' ) )
1568 i_font_color = strtol( psz_value+1, NULL, 16 );
1569 i_font_color &= 0x00ffffff;
1571 else if( !strcasecmp( "alpha", psz_name ) &&
1572 ( psz_value[0] == '#' ) )
1574 i_font_alpha = strtol( psz_value+1, NULL, 16 );
1575 i_font_alpha &= 0xff;
1581 PushFont( &p_fonts, psz_fontname, i_font_size, i_font_color, i_font_alpha );
1582 free( psz_fontname );
1584 else if( !strcasecmp( "b", psz_node ) )
1588 else if( !strcasecmp( "i", psz_node ) )
1590 b_italic = VLC_TRUE;
1592 else if( !strcasecmp( "u", psz_node ) )
1596 else if( !strcasecmp( "br", psz_node ) )
1601 if( !(p_line = NewLine( (byte_t *)psz_html )) )
1603 msg_Err( p_filter, "out of memory" );
1608 p_line->b_new_color_mode = VLC_TRUE;
1609 p_result->x = __MAX( p_result->x, tmp_result.x );
1610 p_result->y += tmp_result.y;
1612 p_line->p_next = NULL;
1614 i_pen_y += tmp_result.y;
1616 p_prev->p_next = p_line;
1624 case XML_READER_TEXT:
1625 psz_node = xml_ReaderValue( p_xml_reader );
1628 char *psz_fontname = NULL;
1629 int i_font_color = 0xffffff;
1630 int i_font_alpha = 0;
1631 int i_font_size = 24;
1632 FT_Face p_face = NULL;
1634 if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ) )
1637 char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig, psz_fontname, b_bold, b_italic, &i_idx );
1641 if( FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
1644 free( psz_fontfile );
1649 free( psz_fontfile );
1653 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face, ft_encoding_unicode ) ||
1654 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0, i_font_size ) )
1660 p_sys->i_use_kerning = FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1662 uint32_t *psz_unicode = IconvText( p_filter, psz_node );
1667 if( p_face ) FT_Done_Face( p_face );
1672 while( *psz_unicode )
1676 if( !(p_line = NewLine( (byte_t *)psz_html )) )
1678 msg_Err( p_filter, "out of memory" );
1680 if( p_face ) FT_Done_Face( p_face );
1684 /* New Color mode only works in YUVA rendering mode --
1685 * (RGB mode has palette constraints on it). We therefore
1686 * need to populate the legacy colour fields also.
1688 p_line->b_new_color_mode = VLC_TRUE;
1689 p_line->i_alpha = i_font_alpha;
1690 p_line->i_red = ( i_font_color & 0xff0000 ) >> 16;
1691 p_line->i_green = ( i_font_color & 0x00ff00 ) >> 8;
1692 p_line->i_blue = ( i_font_color & 0x0000ff );
1693 p_line->p_next = NULL;
1695 i_pen_y += tmp_result.y;
1699 if( p_prev ) p_prev->p_next = p_line;
1700 else *p_lines = p_line;
1703 if( RenderTag( p_filter, p_face, i_font_color, b_uline, p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn, &tmp_result ) != VLC_SUCCESS )
1706 if( p_face ) FT_Done_Face( p_face );
1712 p_result->x = __MAX( p_result->x, tmp_result.x );
1713 p_result->y += tmp_result.y;
1719 if( rv != VLC_SUCCESS ) break;
1721 if( p_face ) FT_Done_Face( p_face );
1722 free( psz_unicode );
1730 p_result->x = __MAX( p_result->x, tmp_result.x );
1731 p_result->y += tmp_result.y;
1735 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1741 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
1742 subpicture_region_t *p_region_in )
1744 int rv = VLC_SUCCESS;
1745 stream_t *p_sub = NULL;
1746 xml_t *p_xml = NULL;
1747 xml_reader_t *p_xml_reader = NULL;
1749 if( !p_region_in || !p_region_in->psz_html )
1750 return VLC_EGENERIC;
1752 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
1753 (uint8_t *) p_region_in->psz_html,
1754 strlen( p_region_in->psz_html ),
1758 p_xml = xml_Create( p_filter );
1761 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
1765 line_desc_t *p_lines = NULL;
1767 rv = ProcessNodes( p_filter, p_xml_reader, p_region_in->psz_html, p_region_in->p_style, &p_lines, &result );
1769 if( rv == VLC_SUCCESS )
1771 p_region_out->i_x = p_region_in->i_x;
1772 p_region_out->i_y = p_region_in->i_y;
1774 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1775 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1777 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1779 FreeLines( p_lines );
1781 xml_ReaderDelete( p_xml, p_xml_reader );
1783 xml_Delete( p_xml );
1785 stream_Delete( p_sub );
1791 static char* FontConfig_Select( FcConfig* priv, const char* family, vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
1794 FcPattern *pat, *p_pat;
1798 pat = FcPatternCreate();
1799 if (!pat) return NULL;
1801 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
1802 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
1803 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
1804 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
1806 FcDefaultSubstitute( pat );
1808 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
1810 FcPatternDestroy( pat );
1814 p_pat = FcFontMatch( priv, pat, &result );
1815 FcPatternDestroy( pat );
1816 if( !p_pat ) return NULL;
1818 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) ) ||
1819 ( val_b != FcTrue ) )
1821 FcPatternDestroy( p_pat );
1824 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
1829 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
1831 FcPatternDestroy( p_pat );
1836 if( strcasecmp((const char*)val_s, family ) != 0 )
1837 msg_Warn( p_filter, "fontconfig: selected font family is not the requested one: '%s' != '%s'\n",
1838 (const char*)val_s, family );
1841 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
1843 FcPatternDestroy( p_pat );
1847 FcPatternDestroy( p_pat );
1848 return strdup( (const char*)val_s );
1852 static void FreeLine( line_desc_t *p_line )
1855 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
1857 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1859 free( p_line->pp_glyphs );
1860 free( p_line->p_glyph_pos );
1861 free( p_line->p_rgb );
1862 free( p_line->pi_underline_offset );
1863 free( p_line->pi_underline_thickness );
1867 static void FreeLines( line_desc_t *p_lines )
1869 line_desc_t *p_line, *p_next;
1871 if( !p_lines ) return;
1873 for( p_line = p_lines; p_line != NULL; p_line = p_next )
1875 p_next = p_line->p_next;
1880 static line_desc_t *NewLine( byte_t *psz_string )
1883 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1885 if( !p_line ) return NULL;
1886 p_line->i_height = 0;
1887 p_line->i_width = 0;
1888 p_line->p_next = NULL;
1890 /* We don't use CountUtf8Characters() here because we are not acutally
1891 * sure the string is utf8. Better be safe than sorry. */
1892 i_count = strlen( (char *)psz_string );
1894 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1895 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1896 p_line->p_rgb = malloc( sizeof( uint32_t ) * i_count + 1 );
1897 p_line->pi_underline_offset = calloc( i_count+ + 1, sizeof( uint16_t ) );
1898 p_line->pi_underline_thickness = calloc( i_count+ + 1, sizeof( uint16_t ) );
1899 if( ( p_line->pp_glyphs == NULL ) ||
1900 ( p_line->p_glyph_pos == NULL ) ||
1901 ( p_line->p_rgb == NULL ) ||
1902 ( p_line->pi_underline_offset == NULL ) ||
1903 ( p_line->pi_underline_thickness == NULL ) )
1905 if( p_line->pi_underline_thickness ) free( p_line->pi_underline_thickness );
1906 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
1907 if( p_line->p_rgb ) free( p_line->p_rgb );
1908 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
1909 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
1913 p_line->pp_glyphs[0] = NULL;
1914 p_line->b_new_color_mode = VLC_FALSE;
1919 static int SetFontSize( filter_t *p_filter, int i_size )
1921 filter_sys_t *p_sys = p_filter->p_sys;
1923 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1929 if( !p_sys->i_default_font_size &&
1930 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1933 if( p_sys->i_default_font_size )
1935 i_size = p_sys->i_default_font_size;
1939 var_Get( p_filter, "freetype-rel-fontsize", &val );
1940 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1941 p_filter->p_sys->i_display_height =
1942 p_filter->fmt_out.video.i_height;
1946 msg_Warn( p_filter, "invalid fontsize, using 12" );
1950 msg_Dbg( p_filter, "using fontsize: %i", i_size );
1953 p_sys->i_font_size = i_size;
1955 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1957 msg_Err( p_filter, "couldn't set font size to %d", i_size );
1958 return VLC_EGENERIC;