1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2011 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
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 Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_stream.h> /* stream_MemoryNew */
37 #include <vlc_input.h> /* vlc_input_attachment_* */
38 #include <vlc_xml.h> /* xml_reader */
39 #include <vlc_strings.h> /* resolve_xml_special_chars */
40 #include <vlc_charset.h> /* ToCharset */
41 #include <vlc_dialog.h> /* FcCache dialog */
45 # define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
46 # define FC_DEFAULT_FONT "Arial Black"
47 #elif defined( WIN32 )
48 # define DEFAULT_FONT "arial.ttf" /* Default font found at run-time */
49 # define FC_DEFAULT_FONT "Arial"
50 #elif defined( HAVE_MAEMO )
51 # define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
52 # define FC_DEFAULT_FONT "Nokia Sans Bold"
54 # define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
55 # define FC_DEFAULT_FONT "Serif Bold"
59 #include <freetype/ftsynth.h>
60 #include FT_FREETYPE_H
62 #define FT_FLOOR(X) ((X & -64) >> 6)
63 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
65 #define FT_MulFix(v, s) (((v)*(s))>>16)
69 #if defined(HAVE_FRIBIDI)
70 # include <fribidi/fribidi.h>
75 # define _WIN32_IE 0x0500
79 # undef HAVE_FONTCONFIG
83 #ifdef HAVE_FONTCONFIG
84 # include <fontconfig/fontconfig.h>
88 /* If we have font/styles support, use a fontfamily */
91 # define DEFAULT_FONT FC_DEFAULT_FONT
96 /*****************************************************************************
98 *****************************************************************************/
99 static int Create ( vlc_object_t * );
100 static void Destroy( vlc_object_t * );
102 #define FONT_TEXT N_("Font")
105 #define FONT_LONGTEXT N_("Font family for the font you want to use")
107 #define FONT_LONGTEXT N_("Font file for the font you want to use")
110 #define FONTSIZE_TEXT N_("Font size in pixels")
111 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
112 "that will be rendered on the video. " \
113 "If set to something different than 0 this option will override the " \
114 "relative font size." )
115 #define OPACITY_TEXT N_("Opacity")
116 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
117 "text that will be rendered on the video. 0 = transparent, " \
118 "255 = totally opaque. " )
119 #define COLOR_TEXT N_("Text default color")
120 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
121 "the video. This must be an hexadecimal (like HTML colors). The first two "\
122 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
123 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
124 #define FONTSIZER_TEXT N_("Relative font size")
125 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
126 "fonts that will be rendered on the video. If absolute font size is set, "\
127 "relative size will be overridden." )
129 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
130 static const char *const ppsz_sizes_text[] = {
131 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
132 #define YUVP_TEXT N_("Use YUVP renderer")
133 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
134 "This option is only needed if you want to encode into DVB subtitles" )
135 #define EFFECT_TEXT N_("Font Effect")
136 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
137 "text to improve its readability." )
139 enum { EFFECT_BACKGROUND = 1,
141 EFFECT_OUTLINE_FAT = 3,
143 static int const pi_effects[] = { EFFECT_BACKGROUND, EFFECT_OUTLINE, EFFECT_OUTLINE_FAT };
144 static const char *const ppsz_effects_text[] = {
145 N_("Background"),N_("Outline"), N_("Fat Outline") };
147 static const int pi_color_values[] = {
148 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
149 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
150 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
152 static const char *const ppsz_color_descriptions[] = {
153 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
154 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
155 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
158 set_shortname( N_("Text renderer"))
159 set_description( N_("Freetype2 font renderer") )
160 set_category( CAT_VIDEO )
161 set_subcategory( SUBCAT_VIDEO_SUBPIC )
163 add_font( "freetype-font", DEFAULT_FONT, FONT_TEXT, FONT_LONGTEXT, false )
165 add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
166 FONTSIZE_LONGTEXT, true )
169 /* opacity valid on 0..255, with default 255 = fully opaque */
170 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
171 OPACITY_TEXT, OPACITY_LONGTEXT, false )
174 /* hook to the color values list, with default 0x00ffffff = white */
175 add_integer( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
176 COLOR_LONGTEXT, false )
177 change_integer_list( pi_color_values, ppsz_color_descriptions )
180 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
181 FONTSIZER_LONGTEXT, false )
182 change_integer_list( pi_sizes, ppsz_sizes_text )
185 add_integer( "freetype-effect", 2, EFFECT_TEXT,
186 EFFECT_LONGTEXT, false )
187 change_integer_list( pi_effects, ppsz_effects_text )
190 add_bool( "freetype-yuvp", false, YUVP_TEXT,
191 YUVP_LONGTEXT, true )
192 set_capability( "text renderer", 100 )
193 add_shortcut( "text" )
194 set_callbacks( Create, Destroy )
198 /*****************************************************************************
200 *****************************************************************************/
202 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
203 static int RenderText( filter_t *, subpicture_region_t *,
204 subpicture_region_t * );
207 static int RenderHtml( filter_t *, subpicture_region_t *,
208 subpicture_region_t * );
210 #ifdef HAVE_FONTCONFIG
211 static void FontConfig_BuildCache( filter_t * );
212 static char *FontConfig_Select( FcConfig *, const char *,
213 bool, bool, int, int * );
216 static char* Win32_Select( filter_t *p_filter, const char* family, bool b_bold,
217 bool b_italic, int i_size, int *i_idx );
220 static int LoadFontsFromAttachments( filter_t *p_filter );
222 static int GetFontSize( filter_t *p_filter );
223 static int SetFontSize( filter_t *, int );
224 static void YUVFromRGB( uint32_t i_argb,
225 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
227 typedef struct line_desc_t line_desc_t;
230 /** NULL-terminated list of glyphs making the string */
231 FT_BitmapGlyph *pp_glyphs;
232 /** list of relative positions for the glyphs */
233 FT_Vector *p_glyph_pos;
234 /** list of RGB information for styled text
235 * -- if the rendering mode supports it (RenderYUVA) and
236 * b_new_color_mode is set, then it becomes possible to
237 * have multicoloured text within the subtitles. */
240 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
241 bool b_new_color_mode;
242 /** underline information -- only supplied if text should be underlined */
243 int *pi_underline_offset;
244 uint16_t *pi_underline_thickness;
248 int i_red, i_green, i_blue;
253 static line_desc_t *NewLine( int );
258 uint32_t i_font_color; /* ARGB */
259 uint32_t i_karaoke_bg_color; /* ARGB */
267 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
268 static void FreeLines( line_desc_t * );
269 static void FreeLine( line_desc_t * );
271 /*****************************************************************************
272 * filter_sys_t: freetype local data
273 *****************************************************************************
274 * This structure is part of the video output thread descriptor.
275 * It describes the freetype specific properties of an output thread.
276 *****************************************************************************/
279 FT_Library p_library; /* handle to library */
280 FT_Face p_face; /* handle to face object */
282 uint8_t i_font_opacity;
287 int i_default_font_size;
288 int i_display_height;
289 char* psz_fontfamily;
293 char* psz_win_fonts_path;
297 input_attachment_t **pp_font_attachments;
298 int i_font_attachments;
301 #define UCHAR uint32_t
302 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
303 #define TR_FONT_STYLE_PTR ft_style_t *
305 #include "text_renderer.h"
307 /*****************************************************************************
308 * Create: allocates osd-text video thread output method
309 *****************************************************************************
310 * This function allocates and initializes a Clone vout method.
311 *****************************************************************************/
312 static int Create( vlc_object_t *p_this )
314 filter_t *p_filter = (filter_t *)p_this;
316 char *psz_fontfile = NULL;
317 char *psz_fontfamily = NULL;
318 int i_error = 0, fontindex = 0;
320 /* Allocate structure */
321 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
326 p_sys->psz_fontfamily = NULL;
330 p_sys->p_library = 0;
331 p_sys->i_font_size = 0;
332 p_sys->i_display_height = 0;
334 var_Create( p_filter, "freetype-rel-fontsize",
335 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
337 psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
338 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
339 p_sys->i_effect = var_InheritInteger( p_filter, "freetype-effect" );
340 p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
341 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
342 p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
343 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
346 /* Get Windows Font folder */
347 wchar_t wdir[MAX_PATH];
348 if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
350 GetWindowsDirectoryW( wdir, MAX_PATH );
351 wcscat( wdir, L"\\fonts" );
353 p_sys->psz_win_fonts_path = FromWide( wdir );
356 /* Set default psz_fontfamily */
357 if( !psz_fontfamily || !*psz_fontfamily )
359 free( psz_fontfamily );
361 psz_fontfamily = strdup( DEFAULT_FONT );
363 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
364 if( !psz_fontfamily )
367 strcat( psz_fontfamily, p_sys->psz_win_fonts_path );
368 strcat( psz_fontfamily, DEFAULT_FONT );
370 strcpy( psz_fontfamily, DEFAULT_FONT );
372 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
375 p_sys->psz_fontfamily = psz_fontfamily;
377 /* Set the font file */
379 #ifdef HAVE_FONTCONFIG
380 FontConfig_BuildCache( p_filter );
383 psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
384 p_sys->i_default_font_size, &fontindex );
386 psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
387 p_sys->i_default_font_size, &fontindex );
390 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
392 psz_fontfile = psz_fontfamily;
393 #else /* !HAVE_STYLES */
394 psz_fontfile = psz_fontfamily;
398 i_error = FT_Init_FreeType( &p_sys->p_library );
401 msg_Err( p_filter, "couldn't initialize freetype" );
405 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
406 fontindex, &p_sys->p_face );
408 if( i_error == FT_Err_Unknown_File_Format )
410 msg_Err( p_filter, "file %s have unknown format",
411 psz_fontfile ? psz_fontfile : "(null)" );
416 msg_Err( p_filter, "failed to load font file %s",
417 psz_fontfile ? psz_fontfile : "(null)" );
421 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
424 msg_Err( p_filter, "font has no unicode translation table" );
428 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
430 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
433 p_sys->pp_font_attachments = NULL;
434 p_sys->i_font_attachments = 0;
436 p_filter->pf_render_text = RenderText;
438 p_filter->pf_render_html = RenderHtml;
440 p_filter->pf_render_html = NULL;
443 LoadFontsFromAttachments( p_filter );
448 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
449 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
450 free( psz_fontfamily );
455 /*****************************************************************************
456 * Destroy: destroy Clone video thread output method
457 *****************************************************************************
458 * Clean up all data and library connections
459 *****************************************************************************/
460 static void Destroy( vlc_object_t *p_this )
462 filter_t *p_filter = (filter_t *)p_this;
463 filter_sys_t *p_sys = p_filter->p_sys;
465 if( p_sys->pp_font_attachments )
469 for( k = 0; k < p_sys->i_font_attachments; k++ )
470 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
472 free( p_sys->pp_font_attachments );
476 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
478 free( p_sys->psz_fontfamily );
480 /* FcFini asserts calling the subfunction FcCacheFini()
481 * even if no other library functions have been made since FcInit(),
482 * so don't call it. */
484 FT_Done_Face( p_sys->p_face );
485 FT_Done_FreeType( p_sys->p_library );
489 /*****************************************************************************
490 * Make any TTF/OTF fonts present in the attachments of the media file
491 * and store them for later use by the FreeType Engine
492 *****************************************************************************/
493 static int LoadFontsFromAttachments( filter_t *p_filter )
495 filter_sys_t *p_sys = p_filter->p_sys;
496 input_attachment_t **pp_attachments;
497 int i_attachments_cnt;
499 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
502 p_sys->i_font_attachments = 0;
503 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
504 if( !p_sys->pp_font_attachments )
507 for( int k = 0; k < i_attachments_cnt; k++ )
509 input_attachment_t *p_attach = pp_attachments[k];
511 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
512 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
513 p_attach->i_data > 0 && p_attach->p_data )
515 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
519 vlc_input_attachment_Delete( p_attach );
522 free( pp_attachments );
527 /*****************************************************************************
528 * Render: place string in picture
529 *****************************************************************************
530 * This function merges the previously rendered freetype glyphs into a picture
531 *****************************************************************************/
532 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
533 line_desc_t *p_line, int i_width, int i_height )
535 VLC_UNUSED(p_filter);
536 static const uint8_t pi_gamma[16] =
537 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
538 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
542 int i, x, y, i_pitch;
543 uint8_t i_y; /* YUV values, derived from incoming RGB */
546 /* Create a new subpicture region */
547 memset( &fmt, 0, sizeof(video_format_t) );
548 fmt.i_chroma = VLC_CODEC_YUVP;
549 fmt.i_width = fmt.i_visible_width = i_width + 4;
550 fmt.i_height = fmt.i_visible_height = i_height + 4;
551 if( p_region->fmt.i_visible_width > 0 )
552 fmt.i_visible_width = p_region->fmt.i_visible_width;
553 if( p_region->fmt.i_visible_height > 0 )
554 fmt.i_visible_height = p_region->fmt.i_visible_height;
555 fmt.i_x_offset = fmt.i_y_offset = 0;
557 assert( !p_region->p_picture );
558 p_region->p_picture = picture_NewFromFormat( &fmt );
559 if( !p_region->p_picture )
561 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
564 /* Calculate text color components */
565 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
566 25 * p_line->i_blue + 128) >> 8) + 16;
567 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
568 112 * p_line->i_blue + 128) >> 8) + 128;
569 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
570 18 * p_line->i_blue + 128) >> 8) + 128;
573 fmt.p_palette->i_entries = 16;
574 for( i = 0; i < 8; i++ )
576 fmt.p_palette->palette[i][0] = 0;
577 fmt.p_palette->palette[i][1] = 0x80;
578 fmt.p_palette->palette[i][2] = 0x80;
579 fmt.p_palette->palette[i][3] = pi_gamma[i];
580 fmt.p_palette->palette[i][3] =
581 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
583 for( i = 8; i < fmt.p_palette->i_entries; i++ )
585 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
586 fmt.p_palette->palette[i][1] = i_u;
587 fmt.p_palette->palette[i][2] = i_v;
588 fmt.p_palette->palette[i][3] = pi_gamma[i];
589 fmt.p_palette->palette[i][3] =
590 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
593 p_dst = p_region->p_picture->Y_PIXELS;
594 i_pitch = p_region->p_picture->Y_PITCH;
596 /* Initialize the region pixels */
597 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
599 for( ; p_line != NULL; p_line = p_line->p_next )
601 int i_glyph_tmax = 0;
602 int i_bitmap_offset, i_offset, i_align_offset = 0;
603 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
605 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
606 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
609 if( p_line->i_width < i_width )
611 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
613 i_align_offset = i_width - p_line->i_width;
615 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
617 i_align_offset = ( i_width - p_line->i_width ) / 2;
621 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
623 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
625 i_offset = ( p_line->p_glyph_pos[ i ].y +
626 i_glyph_tmax - p_glyph->top + 2 ) *
627 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
630 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
632 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
634 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
636 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
643 /* Outlining (find something better than nearest neighbour filtering ?) */
646 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
647 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
648 uint8_t left, current;
650 for( y = 1; y < (int)fmt.i_height - 1; y++ )
652 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
653 p_dst += p_region->p_picture->Y_PITCH;
656 for( x = 1; x < (int)fmt.i_width - 1; x++ )
659 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
660 p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
664 memset( p_top, 0, fmt.i_width );
670 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
671 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
672 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
673 int i_glyph_tmax, int i_align_offset,
674 uint8_t i_y, uint8_t i_u, uint8_t i_v,
675 subpicture_region_t *p_region)
679 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
681 p_dst_y = p_region->p_picture->Y_PIXELS;
682 p_dst_u = p_region->p_picture->U_PIXELS;
683 p_dst_v = p_region->p_picture->V_PIXELS;
684 p_dst_a = p_region->p_picture->A_PIXELS;
685 i_pitch = p_region->p_picture->A_PITCH;
687 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
688 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
690 for( y = 0; y < i_line_thickness; y++ )
692 int i_extra = p_this_glyph->bitmap.width;
696 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
697 (p_this_glyph_pos->x + p_this_glyph->left);
699 for( x = 0; x < i_extra; x++ )
703 /* break the underline around the tails of any glyphs which cross it */
704 /* Strikethrough doesn't get broken */
705 for( z = x - i_line_thickness;
706 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
709 if( p_next_glyph && ( z >= i_extra ) )
711 int i_row = i_line_offset + p_next_glyph->top + y;
713 if( ( p_next_glyph->bitmap.rows > i_row ) &&
714 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
719 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
721 int i_row = i_line_offset + p_this_glyph->top + y;
723 if( ( p_this_glyph->bitmap.rows > i_row ) &&
724 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
733 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
734 p_dst_u[i_offset+x] = i_u;
735 p_dst_v[i_offset+x] = i_v;
736 p_dst_a[i_offset+x] = 255;
743 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
745 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
746 int i_pitch = p_region->p_picture->A_PITCH;
749 for( ; p_line != NULL; p_line = p_line->p_next )
751 int i_glyph_tmax=0, i = 0;
752 int i_bitmap_offset, i_offset, i_align_offset = 0;
753 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
755 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
756 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
759 if( p_line->i_width < i_width )
761 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
763 i_align_offset = i_width - p_line->i_width;
765 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
767 i_align_offset = ( i_width - p_line->i_width ) / 2;
771 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
773 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
775 i_offset = ( p_line->p_glyph_pos[ i ].y +
776 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
777 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
778 i_align_offset +xoffset;
780 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
782 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
784 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
785 if( p_dst[i_offset+x] <
786 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
788 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
797 /*****************************************************************************
798 * Render: place string in picture
799 *****************************************************************************
800 * This function merges the previously rendered freetype glyphs into a picture
801 *****************************************************************************/
802 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
803 line_desc_t *p_line, int i_width, int i_height )
805 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
807 int i, x, y, i_pitch, i_alpha;
808 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
810 if( i_width == 0 || i_height == 0 )
813 /* Create a new subpicture region */
814 memset( &fmt, 0, sizeof(video_format_t) );
815 fmt.i_chroma = VLC_CODEC_YUVA;
816 fmt.i_width = fmt.i_visible_width = i_width + 6;
817 fmt.i_height = fmt.i_visible_height = i_height + 6;
818 if( p_region->fmt.i_visible_width > 0 )
819 fmt.i_visible_width = p_region->fmt.i_visible_width;
820 if( p_region->fmt.i_visible_height > 0 )
821 fmt.i_visible_height = p_region->fmt.i_visible_height;
822 fmt.i_x_offset = fmt.i_y_offset = 0;
824 p_region->p_picture = picture_NewFromFormat( &fmt );
825 if( !p_region->p_picture )
829 /* Calculate text color components */
830 YUVFromRGB( (p_line->i_red << 16) |
831 (p_line->i_green << 8) |
834 i_alpha = p_line->i_alpha;
836 p_dst_y = p_region->p_picture->Y_PIXELS;
837 p_dst_u = p_region->p_picture->U_PIXELS;
838 p_dst_v = p_region->p_picture->V_PIXELS;
839 p_dst_a = p_region->p_picture->A_PIXELS;
840 i_pitch = p_region->p_picture->A_PITCH;
842 /* Initialize the region pixels */
843 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
845 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
846 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
847 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
848 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
852 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
853 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
854 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
855 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
857 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
858 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
860 DrawBlack( p_line, i_width, p_region, 0, 0);
861 DrawBlack( p_line, i_width, p_region, -1, 0);
862 DrawBlack( p_line, i_width, p_region, 0, -1);
863 DrawBlack( p_line, i_width, p_region, 1, 0);
864 DrawBlack( p_line, i_width, p_region, 0, 1);
867 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
869 DrawBlack( p_line, i_width, p_region, -1, -1);
870 DrawBlack( p_line, i_width, p_region, -1, 1);
871 DrawBlack( p_line, i_width, p_region, 1, -1);
872 DrawBlack( p_line, i_width, p_region, 1, 1);
874 DrawBlack( p_line, i_width, p_region, -2, 0);
875 DrawBlack( p_line, i_width, p_region, 0, -2);
876 DrawBlack( p_line, i_width, p_region, 2, 0);
877 DrawBlack( p_line, i_width, p_region, 0, 2);
879 DrawBlack( p_line, i_width, p_region, -2, -2);
880 DrawBlack( p_line, i_width, p_region, -2, 2);
881 DrawBlack( p_line, i_width, p_region, 2, -2);
882 DrawBlack( p_line, i_width, p_region, 2, 2);
884 DrawBlack( p_line, i_width, p_region, -3, 0);
885 DrawBlack( p_line, i_width, p_region, 0, -3);
886 DrawBlack( p_line, i_width, p_region, 3, 0);
887 DrawBlack( p_line, i_width, p_region, 0, 3);
890 for( ; p_line != NULL; p_line = p_line->p_next )
892 int i_glyph_tmax = 0;
893 int i_bitmap_offset, i_offset, i_align_offset = 0;
894 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
896 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
897 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
900 if( p_line->i_width < i_width )
902 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
904 i_align_offset = i_width - p_line->i_width;
906 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
908 i_align_offset = ( i_width - p_line->i_width ) / 2;
912 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
914 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
916 i_offset = ( p_line->p_glyph_pos[ i ].y +
917 i_glyph_tmax - p_glyph->top + 3 ) *
918 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
921 if( p_line->b_new_color_mode )
923 /* Every glyph can (and in fact must) have its own color */
924 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
927 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
929 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
931 uint8_t i_y_local = i_y;
932 uint8_t i_u_local = i_u;
933 uint8_t i_v_local = i_v;
935 if( p_line->p_fg_bg_ratio != 0x00 )
937 int i_split = p_glyph->bitmap.width *
938 p_line->p_fg_bg_ratio[ i ] / 0x7f;
942 YUVFromRGB( p_line->p_bg_rgb[ i ],
943 &i_y_local, &i_u_local, &i_v_local );
947 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
949 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
950 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
952 p_dst_u[i_offset+x] = i_u;
953 p_dst_v[i_offset+x] = i_v;
955 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
956 p_dst_a[i_offset+x] = 0xff;
962 if( p_line->pi_underline_thickness[ i ] )
964 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
965 p_line->pi_underline_offset[ i ],
966 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
967 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
968 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
969 i_glyph_tmax, i_align_offset,
976 /* Apply the alpha setting */
977 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
978 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
984 * This function renders a text subpicture region into another one.
985 * It also calculates the size needed for this string, and renders the
986 * needed glyphs into memory. It is used as pf_add_string callback in
987 * the vout method by this module
989 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
990 subpicture_region_t *p_region_in )
992 filter_sys_t *p_sys = p_filter->p_sys;
993 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
994 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
995 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
996 size_t i_string_length;
998 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1008 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1009 psz_string = p_region_in->psz_text;
1010 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1012 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1013 i_scale = val.i_int;
1015 if( p_region_in->p_style )
1017 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1018 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1019 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1023 i_font_color = p_sys->i_font_color;
1024 i_font_alpha = 255 - p_sys->i_font_opacity;
1025 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1028 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1029 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1030 SetFontSize( p_filter, i_font_size );
1032 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1033 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1034 i_blue = i_font_color & 0x000000FF;
1036 result.x = result.y = 0;
1037 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1039 #if defined(WORDS_BIGENDIAN)
1040 psz_unicode = ToCharset( "UCS-4BE", psz_string, &i_string_length );
1042 psz_unicode = ToCharset( "UCS-4LE", psz_string, &i_string_length );
1044 if( psz_unicode == NULL )
1046 psz_unicode_orig = psz_unicode;
1047 i_string_length /= 4;
1049 #if defined(HAVE_FRIBIDI)
1051 uint32_t *p_fribidi_string;
1055 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1056 if( !p_fribidi_string )
1059 /* Do bidi conversion line-by-line */
1060 while( pos < i_string_length )
1062 while( pos < i_string_length )
1064 i_char = psz_unicode[pos];
1065 if (i_char != '\r' && i_char != '\n')
1067 p_fribidi_string[pos] = i_char;
1071 while( pos < i_string_length )
1073 i_char = psz_unicode[pos];
1074 if (i_char == '\r' || i_char == '\n')
1078 if (pos > start_pos)
1080 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1081 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1083 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1085 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1088 (FriBidiChar*)p_fribidi_string + start_pos,
1093 free( psz_unicode_orig );
1094 psz_unicode = psz_unicode_orig = p_fribidi_string;
1095 p_fribidi_string[ i_string_length ] = 0;
1099 /* Calculate relative glyph positions and a bounding box for the
1101 if( !(p_line = NewLine( strlen( psz_string ))) )
1104 i_pen_x = i_pen_y = 0;
1106 psz_line_start = psz_unicode;
1108 #define face p_sys->p_face
1109 #define glyph face->glyph
1111 while( *psz_unicode )
1113 i_char = *psz_unicode++;
1114 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1119 if( i_char == '\n' )
1121 psz_line_start = psz_unicode;
1122 if( !(p_next = NewLine( strlen( psz_string ))) )
1124 p_line->p_next = p_next;
1125 p_line->i_width = line.xMax;
1126 p_line->i_height = face->size->metrics.height >> 6;
1127 p_line->pp_glyphs[ i ] = NULL;
1128 p_line->i_alpha = i_font_alpha;
1129 p_line->i_red = i_red;
1130 p_line->i_green = i_green;
1131 p_line->i_blue = i_blue;
1134 result.x = __MAX( result.x, line.xMax );
1135 result.y += face->size->metrics.height >> 6;
1138 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1139 i_pen_y += face->size->metrics.height >> 6;
1141 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1146 i_glyph_index = FT_Get_Char_Index( face, i_char );
1147 if( p_sys->i_use_kerning && i_glyph_index
1151 FT_Get_Kerning( face, i_previous, i_glyph_index,
1152 ft_kerning_default, &delta );
1153 i_pen_x += delta.x >> 6;
1156 p_line->p_glyph_pos[ i ].x = i_pen_x;
1157 p_line->p_glyph_pos[ i ].y = i_pen_y;
1158 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1161 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1164 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1169 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1172 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1176 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1177 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1180 FT_Done_Glyph( tmp_glyph );
1183 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1186 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1187 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1188 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1190 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1191 p_line->pp_glyphs[ i ] = NULL;
1193 p_line = NewLine( strlen( psz_string ));
1194 if( p_prev ) p_prev->p_next = p_line;
1195 else p_lines = p_line;
1197 uint32_t *psz_unicode_saved = psz_unicode;
1198 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1202 if( psz_unicode == psz_line_start )
1203 { /* try harder to break that line */
1204 psz_unicode = psz_unicode_saved;
1205 while( psz_unicode > psz_line_start &&
1206 *psz_unicode != '_' && *psz_unicode != '/' &&
1207 *psz_unicode != '\\' && *psz_unicode != '.' )
1212 if( psz_unicode == psz_line_start )
1214 msg_Warn( p_filter, "unbreakable string" );
1219 *psz_unicode = '\n';
1221 psz_unicode = psz_line_start;
1224 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1227 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1228 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1230 i_previous = i_glyph_index;
1231 i_pen_x += glyph->advance.x >> 6;
1235 p_line->i_width = line.xMax;
1236 p_line->i_height = face->size->metrics.height >> 6;
1237 p_line->pp_glyphs[ i ] = NULL;
1238 p_line->i_alpha = i_font_alpha;
1239 p_line->i_red = i_red;
1240 p_line->i_green = i_green;
1241 p_line->i_blue = i_blue;
1242 result.x = __MAX( result.x, line.xMax );
1243 result.y += line.yMax - line.yMin;
1248 p_region_out->i_x = p_region_in->i_x;
1249 p_region_out->i_y = p_region_in->i_y;
1251 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1252 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1254 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1256 free( psz_unicode_orig );
1257 FreeLines( p_lines );
1261 free( psz_unicode_orig );
1262 FreeLines( p_lines );
1263 return VLC_EGENERIC;
1267 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1268 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1269 bool b_italic, bool b_uline, bool b_through )
1271 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1275 p_style->i_font_size = i_font_size;
1276 p_style->i_font_color = i_font_color;
1277 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1278 p_style->b_italic = b_italic;
1279 p_style->b_bold = b_bold;
1280 p_style->b_underline = b_uline;
1281 p_style->b_through = b_through;
1283 p_style->psz_fontname = strdup( psz_fontname );
1288 static void DeleteStyle( ft_style_t *p_style )
1292 free( p_style->psz_fontname );
1297 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1304 if(( s1->i_font_size == s2->i_font_size ) &&
1305 ( s1->i_font_color == s2->i_font_color ) &&
1306 ( s1->b_italic == s2->b_italic ) &&
1307 ( s1->b_through == s2->b_through ) &&
1308 ( s1->b_bold == s2->b_bold ) &&
1309 ( s1->b_underline == s2->b_underline ) &&
1310 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1317 static void IconvText( filter_t *p_filter, const char *psz_string,
1318 size_t *i_string_length, uint32_t **ppsz_unicode )
1320 *i_string_length = 0;
1321 if( *ppsz_unicode == NULL )
1326 #if defined(WORDS_BIGENDIAN)
1327 ToCharset( "UCS-4BE", psz_string, &i_length );
1329 ToCharset( "UCS-4LE", psz_string, &i_length );
1333 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1336 memcpy( *ppsz_unicode, psz_tmp, i_length );
1337 *i_string_length = i_length / 4;
1342 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1343 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1344 bool b_uline, bool b_through )
1346 ft_style_t *p_style = NULL;
1348 char *psz_fontname = NULL;
1349 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1350 uint32_t i_karaoke_bg_color = i_font_color;
1351 int i_font_size = p_sys->i_font_size;
1353 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1354 &i_font_color, &i_karaoke_bg_color ))
1356 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1357 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1362 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1363 bool b_uline, bool b_through, bool b_bold,
1364 bool b_italic, int i_karaoke_bgcolor,
1365 line_desc_t *p_line, uint32_t *psz_unicode,
1366 int *pi_pen_x, int i_pen_y, int *pi_start,
1367 FT_Vector *p_result )
1372 bool b_first_on_line = true;
1375 int i_pen_x_start = *pi_pen_x;
1377 uint32_t *psz_unicode_start = psz_unicode;
1379 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1381 /* Account for part of line already in position */
1382 for( i=0; i<*pi_start; i++ )
1386 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1387 ft_glyph_bbox_pixels, &glyph_size );
1389 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1390 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1391 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1392 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1398 b_first_on_line = false;
1400 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1406 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1407 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1411 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1412 ft_kerning_default, &delta );
1413 *pi_pen_x += delta.x >> 6;
1415 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1416 p_line->p_glyph_pos[ i ].y = i_pen_y;
1418 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1421 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1425 "unable to render text FT_Load_Glyph returned %d", i_error );
1426 p_line->pp_glyphs[ i ] = NULL;
1427 return VLC_EGENERIC;
1431 /* Do synthetic styling now that Freetype supports it;
1432 * ie. if the font we have loaded is NOT already in the
1433 * style that the tags want, then switch it on; if they
1434 * are then don't. */
1435 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1436 FT_GlyphSlot_Embolden( p_face->glyph );
1437 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1438 FT_GlyphSlot_Oblique( p_face->glyph );
1440 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1444 "unable to render text FT_Get_Glyph returned %d", i_error );
1445 p_line->pp_glyphs[ i ] = NULL;
1446 return VLC_EGENERIC;
1448 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1449 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1452 FT_Done_Glyph( tmp_glyph );
1455 if( b_uline || b_through )
1457 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1458 p_face->size->metrics.y_scale));
1459 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1460 p_face->size->metrics.y_scale));
1462 p_line->pi_underline_offset[ i ] =
1463 ( aOffset < 0 ) ? -aOffset : aOffset;
1464 p_line->pi_underline_thickness[ i ] =
1465 ( aSize < 0 ) ? -aSize : aSize;
1468 /* Move the baseline to make it strikethrough instead of
1469 * underline. That means that strikethrough takes precedence
1471 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1472 p_face->size->metrics.y_scale));
1474 p_line->pi_underline_offset[ i ] -=
1475 ( aDescent < 0 ) ? -aDescent : aDescent;
1479 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1480 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1481 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1482 p_line->p_fg_bg_ratio[ i ] = 0x00;
1484 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1485 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1486 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1488 for( ; i >= *pi_start; i-- )
1489 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1492 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1496 if( psz_unicode == psz_unicode_start )
1498 if( b_first_on_line )
1500 msg_Warn( p_filter, "unbreakable string" );
1501 p_line->pp_glyphs[ i ] = NULL;
1502 return VLC_EGENERIC;
1504 *pi_pen_x = i_pen_x_start;
1506 p_line->i_width = line.xMax;
1507 p_line->i_height = __MAX( p_line->i_height,
1508 p_face->size->metrics.height >> 6 );
1509 p_line->pp_glyphs[ i ] = NULL;
1511 p_result->x = __MAX( p_result->x, line.xMax );
1512 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1513 i_yMax - i_yMin ) );
1518 *psz_unicode = '\n';
1520 psz_unicode = psz_unicode_start;
1521 *pi_pen_x = i_pen_x_start;
1529 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1530 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1532 i_previous = i_glyph_index;
1533 *pi_pen_x += p_face->glyph->advance.x >> 6;
1536 p_line->i_width = line.xMax;
1537 p_line->i_height = __MAX( p_line->i_height,
1538 p_face->size->metrics.height >> 6 );
1539 p_line->pp_glyphs[ i ] = NULL;
1541 p_result->x = __MAX( p_result->x, line.xMax );
1542 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1543 line.yMax - line.yMin ) );
1547 /* Get rid of any text processed - if necessary repositioning
1548 * at the start of a new line of text
1552 *psz_unicode_start = '\0';
1554 else if( psz_unicode > psz_unicode_start )
1556 for( i=0; psz_unicode[ i ]; i++ )
1557 psz_unicode_start[ i ] = psz_unicode[ i ];
1558 psz_unicode_start[ i ] = '\0';
1564 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1565 uint32_t **psz_text_out, uint32_t *pi_runs,
1566 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1567 ft_style_t *p_style )
1569 size_t i_string_length;
1571 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1572 *psz_text_out += i_string_length;
1574 if( ppp_styles && ppi_run_lengths )
1578 /* XXX this logic looks somewhat broken */
1582 *ppp_styles = realloc_or_free( *ppp_styles,
1583 *pi_runs * sizeof( ft_style_t * ) );
1585 else if( *pi_runs == 1 )
1587 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1590 /* We have just malloc'ed this memory successfully -
1591 * *pi_runs HAS to be within the memory area of *ppp_styles */
1594 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1598 /* XXX more iffy logic */
1600 if( *ppi_run_lengths )
1602 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1603 *pi_runs * sizeof( uint32_t ) );
1605 else if( *pi_runs == 1 )
1607 *ppi_run_lengths = (uint32_t *)
1608 malloc( *pi_runs * sizeof( uint32_t ) );
1611 /* same remarks here */
1612 if( *ppi_run_lengths )
1614 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1617 /* If we couldn't use the p_style argument due to memory allocation
1618 * problems above, release it here.
1620 if( p_style ) DeleteStyle( p_style );
1623 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1627 for( k=0; k < p_sys->i_font_attachments; k++ )
1629 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1631 FT_Face p_face = NULL;
1633 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1641 bool match = !strcasecmp( p_face->family_name,
1642 p_style->psz_fontname );
1644 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1645 match = match && p_style->b_bold;
1647 match = match && !p_style->b_bold;
1649 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1650 match = match && p_style->b_italic;
1652 match = match && !p_style->b_italic;
1660 FT_Done_Face( p_face );
1665 return VLC_EGENERIC;
1668 static int ProcessLines( filter_t *p_filter,
1673 uint32_t *pi_run_lengths,
1674 ft_style_t **pp_styles,
1675 line_desc_t **pp_lines,
1677 FT_Vector *p_result,
1681 uint32_t *pi_k_run_lengths,
1682 uint32_t *pi_k_durations )
1684 filter_sys_t *p_sys = p_filter->p_sys;
1685 ft_style_t **pp_char_styles;
1686 int *p_new_positions = NULL;
1687 int8_t *p_levels = NULL;
1688 uint8_t *pi_karaoke_bar = NULL;
1692 /* Assign each character in the text string its style explicitly, so that
1693 * after the characters have been shuffled around by Fribidi, we can re-apply
1694 * the styles, and to simplify the calculation of runs within a line.
1696 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1697 if( !pp_char_styles )
1702 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1703 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1704 * we just won't be able to display the progress bar; at least we'll
1710 for( j = 0; j < i_runs; j++ )
1711 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1712 pp_char_styles[ i++ ] = pp_styles[ j ];
1714 #if defined(HAVE_FRIBIDI)
1716 ft_style_t **pp_char_styles_new;
1717 int *p_old_positions;
1718 uint32_t *p_fribidi_string;
1719 int start_pos, pos = 0;
1721 pp_char_styles_new = (ft_style_t **)
1722 malloc( i_len * sizeof( ft_style_t * ));
1724 p_fribidi_string = (uint32_t *)
1725 malloc( (i_len + 1) * sizeof(uint32_t) );
1726 p_old_positions = (int *)
1727 malloc( (i_len + 1) * sizeof( int ) );
1728 p_new_positions = (int *)
1729 malloc( (i_len + 1) * sizeof( int ) );
1730 p_levels = (int8_t *)
1731 malloc( (i_len + 1) * sizeof( int8_t ) );
1733 if( ! pp_char_styles_new ||
1734 ! p_fribidi_string ||
1735 ! p_old_positions ||
1736 ! p_new_positions ||
1740 free( p_old_positions );
1741 free( p_new_positions );
1742 free( p_fribidi_string );
1743 free( pp_char_styles_new );
1744 free( pi_karaoke_bar );
1746 free( pp_char_styles );
1750 /* Do bidi conversion line-by-line */
1753 while(pos < i_len) {
1754 if (psz_text[pos] != '\n')
1756 p_fribidi_string[pos] = psz_text[pos];
1757 pp_char_styles_new[pos] = pp_char_styles[pos];
1758 p_new_positions[pos] = pos;
1763 while(pos < i_len) {
1764 if (psz_text[pos] == '\n')
1768 if (pos > start_pos)
1770 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1771 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1773 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1775 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1776 pos - start_pos, &base_dir,
1777 (FriBidiChar*)p_fribidi_string + start_pos,
1778 p_new_positions + start_pos,
1780 p_levels + start_pos );
1781 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1783 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1784 p_old_positions[ j - start_pos ] ];
1785 p_new_positions[ j ] += start_pos;
1789 free( p_old_positions );
1790 free( pp_char_styles );
1791 pp_char_styles = pp_char_styles_new;
1792 psz_text = p_fribidi_string;
1793 p_fribidi_string[ i_len ] = 0;
1796 /* Work out the karaoke */
1797 if( pi_karaoke_bar )
1799 int64_t i_last_duration = 0;
1800 int64_t i_duration = 0;
1801 int64_t i_start_pos = 0;
1802 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1804 for( k = 0; k< i_k_runs; k++ )
1806 double fraction = 0.0;
1808 i_duration += pi_k_durations[ k ];
1810 if( i_duration < i_elapsed )
1812 /* Completely finished this run-length -
1813 * let it render normally */
1817 else if( i_elapsed < i_last_duration )
1819 /* Haven't got up to this segment yet -
1820 * render it completely in karaoke BG mode */
1826 /* Partway through this run */
1828 fraction = (double)(i_elapsed - i_last_duration) /
1829 (double)pi_k_durations[ k ];
1831 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1833 double shade = pi_k_run_lengths[ k ] * fraction;
1835 if( p_new_positions )
1836 j = p_new_positions[ i_start_pos + i ];
1838 j = i_start_pos + i;
1840 if( i < (uint32_t)shade )
1841 pi_karaoke_bar[ j ] = 0xff;
1842 else if( (double)i > shade )
1843 pi_karaoke_bar[ j ] = 0x00;
1846 shade -= (int)shade;
1847 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1848 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1852 i_last_duration = i_duration;
1853 i_start_pos += pi_k_run_lengths[ k ];
1857 free( p_new_positions );
1859 FT_Vector tmp_result;
1861 line_desc_t *p_line = NULL;
1862 line_desc_t *p_prev = NULL;
1868 p_result->x = p_result->y = 0;
1869 tmp_result.x = tmp_result.y = 0;
1872 for( k = 0; k <= (uint32_t) i_len; k++ )
1874 if( ( k == (uint32_t) i_len ) ||
1876 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1878 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1880 /* End of the current style run */
1881 FT_Face p_face = NULL;
1884 /* Look for a match amongst our attachments first */
1885 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1889 char *psz_fontfile = NULL;
1891 #ifdef HAVE_FONTCONFIG
1892 psz_fontfile = FontConfig_Select( NULL,
1893 p_style->psz_fontname,
1898 #elif defined( WIN32 )
1899 psz_fontfile = Win32_Select( p_filter,
1900 p_style->psz_fontname,
1908 if( psz_fontfile && ! *psz_fontfile )
1910 msg_Warn( p_filter, "We were not able to find a matching font: \"%s\" %s,"
1911 " so using default font", p_style->psz_fontname,
1912 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1913 (p_style->b_bold ? "(Bold)" :
1914 (p_style->b_italic ? "(Italic)" : ""))) );
1915 free( psz_fontfile );
1916 psz_fontfile = NULL;
1921 if( FT_New_Face( p_sys->p_library,
1922 psz_fontfile, i_idx, &p_face ) )
1924 free( psz_fontfile );
1925 free( pp_char_styles );
1926 #if defined(HAVE_FRIBIDI)
1929 free( pi_karaoke_bar );
1930 return VLC_EGENERIC;
1932 free( psz_fontfile );
1936 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1938 /* We've loaded a font face which is unhelpful for actually
1939 * rendering text - fallback to the default one.
1941 FT_Done_Face( p_face );
1945 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1946 ft_encoding_unicode ) ||
1947 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1948 p_style->i_font_size ) )
1950 if( p_face ) FT_Done_Face( p_face );
1951 free( pp_char_styles );
1952 #if defined(HAVE_FRIBIDI)
1955 free( pi_karaoke_bar );
1956 return VLC_EGENERIC;
1958 p_sys->i_use_kerning =
1959 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1962 uint32_t *psz_unicode = (uint32_t *)
1963 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1966 if( p_face ) FT_Done_Face( p_face );
1967 free( pp_char_styles );
1968 free( psz_unicode );
1969 #if defined(HAVE_FRIBIDI)
1972 free( pi_karaoke_bar );
1975 memcpy( psz_unicode, psz_text + i_prev,
1976 sizeof( uint32_t ) * ( k - i_prev ) );
1977 psz_unicode[ k - i_prev ] = 0;
1978 while( *psz_unicode )
1982 if( !(p_line = NewLine( i_len - i_prev)) )
1984 if( p_face ) FT_Done_Face( p_face );
1985 free( pp_char_styles );
1986 free( psz_unicode );
1987 #if defined(HAVE_FRIBIDI)
1990 free( pi_karaoke_bar );
1993 /* New Color mode only works in YUVA rendering mode --
1994 * (RGB mode has palette constraints on it). We therefore
1995 * need to populate the legacy colour fields also.
1997 p_line->b_new_color_mode = true;
1998 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
1999 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2000 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2001 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2002 p_line->p_next = NULL;
2004 i_pen_y += tmp_result.y;
2008 if( p_prev ) p_prev->p_next = p_line;
2009 else *pp_lines = p_line;
2012 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2013 p_style->i_font_color, p_style->b_underline,
2017 p_style->i_karaoke_bg_color,
2018 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2019 &tmp_result ) != VLC_SUCCESS )
2021 if( p_face ) FT_Done_Face( p_face );
2022 free( pp_char_styles );
2023 free( psz_unicode );
2024 #if defined(HAVE_FRIBIDI)
2027 free( pi_karaoke_bar );
2028 return VLC_EGENERIC;
2033 p_result->x = __MAX( p_result->x, tmp_result.x );
2034 p_result->y += tmp_result.y;
2039 if( *psz_unicode == '\n')
2043 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2045 *c_ptr = *(c_ptr+1);
2050 free( psz_unicode );
2051 if( p_face ) FT_Done_Face( p_face );
2055 free( pp_char_styles );
2056 #if defined(HAVE_FRIBIDI)
2061 p_result->x = __MAX( p_result->x, tmp_result.x );
2062 p_result->y += tmp_result.y;
2065 if( pi_karaoke_bar )
2068 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2070 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2072 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2076 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2078 /* 100% BG colour will render faster if we
2079 * instead make it 100% FG colour, so leave
2080 * the ratio alone and copy the value across
2082 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2086 if( pi_karaoke_bar[ i ] & 0x80 )
2088 /* Swap Left and Right sides over for Right aligned
2089 * language text (eg. Arabic, Hebrew)
2091 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2093 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2094 p_line->p_bg_rgb[ k ] = i_tmp;
2096 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2099 /* Jump over the '\n' at the line-end */
2102 free( pi_karaoke_bar );
2108 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2109 subpicture_region_t *p_region_in )
2111 int rv = VLC_SUCCESS;
2112 stream_t *p_sub = NULL;
2114 if( !p_region_in || !p_region_in->psz_html )
2115 return VLC_EGENERIC;
2117 /* Reset the default fontsize in case screen metrics have changed */
2118 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2120 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2121 (uint8_t *) p_region_in->psz_html,
2122 strlen( p_region_in->psz_html ),
2124 if( unlikely(p_sub == NULL) )
2127 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2128 bool b_karaoke = false;
2131 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2133 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2135 p_filter->p_sys->p_xml = p_xml_reader;
2138 /* Look for Root Node */
2141 if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2143 if( !strcasecmp( "karaoke", node ) )
2145 /* We're going to have to render the text a number
2146 * of times to show the progress marker on the text.
2148 var_SetBool( p_filter, "text-rerender", true );
2151 else if( !strcasecmp( "text", node ) )
2157 /* Only text and karaoke tags are supported */
2158 msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2165 msg_Err( p_filter, "Malformed HTML subtitle" );
2169 if( rv != VLC_SUCCESS )
2171 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2172 p_xml_reader = NULL;
2180 uint32_t i_runs = 0;
2181 uint32_t i_k_runs = 0;
2182 uint32_t *pi_run_lengths = NULL;
2183 uint32_t *pi_k_run_lengths = NULL;
2184 uint32_t *pi_k_durations = NULL;
2185 ft_style_t **pp_styles = NULL;
2186 FT_Vector result = {0, 0};
2187 line_desc_t *p_lines = NULL;
2189 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2190 sizeof( uint32_t ) );
2193 rv = ProcessNodes( p_filter, p_xml_reader,
2194 p_region_in->p_style, psz_text, &i_len,
2195 &i_runs, &pi_run_lengths, &pp_styles,
2196 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2199 p_region_out->i_x = p_region_in->i_x;
2200 p_region_out->i_y = p_region_in->i_y;
2202 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2204 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2205 pi_run_lengths, pp_styles, &p_lines,
2206 &result, b_karaoke, i_k_runs,
2207 pi_k_run_lengths, pi_k_durations );
2210 for( uint_fast32_t k=0; k<i_runs; k++)
2211 DeleteStyle( pp_styles[k] );
2213 free( pi_run_lengths );
2216 /* Don't attempt to render text that couldn't be layed out
2218 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2220 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2221 Render( p_filter, p_region_out, p_lines,
2222 result.x, result.y );
2224 RenderYUVA( p_filter, p_region_out, p_lines,
2225 result.x, result.y );
2228 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2229 FreeLines( p_lines );
2231 stream_Delete( p_sub );
2237 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
2239 static int GetFileFontByName( const char *font_name, char **psz_filename )
2242 wchar_t vbuffer[MAX_PATH];
2243 wchar_t dbuffer[256];
2245 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey) != ERROR_SUCCESS )
2248 for( int index = 0;; index++ )
2250 DWORD vbuflen = MAX_PATH - 1;
2251 DWORD dbuflen = 255;
2253 if( RegEnumValueW( hKey, index, vbuffer, &vbuflen,
2254 NULL, NULL, (LPBYTE)dbuffer, &dbuflen) != ERROR_SUCCESS )
2257 char *psz_value = FromWide( vbuffer );
2259 char *s = strchr( psz_value,'(' );
2260 if( s != NULL && s != psz_value ) s[-1] = '\0';
2262 /* Manage concatenated font names */
2263 if( strchr( psz_value, '&') ) {
2264 if( strcasestr( psz_value, font_name ) != NULL )
2268 if( strcasecmp( psz_value, font_name ) == 0 )
2273 *psz_filename = FromWide( dbuffer );
2278 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
2279 DWORD type, LPARAM lParam)
2281 VLC_UNUSED( metric );
2282 if( (type & RASTER_FONTTYPE) ) return 1;
2283 // if( lpelfe->elfScript ) FIXME
2285 return GetFileFontByName( (const char *)lpelfe->elfFullName, (char **)lParam );
2288 static char* Win32_Select( filter_t *p_filter, const char* family,
2289 bool b_bold, bool b_italic, int i_size, int *i_idx )
2291 VLC_UNUSED( i_size );
2292 // msg_Dbg( p_filter, "Here in Win32_Select, asking for %s", family );
2296 lf.lfCharSet = DEFAULT_CHARSET;
2300 lf.lfWeight = FW_BOLD;
2301 strncpy( (LPSTR)&lf.lfFaceName, family, 32);
2304 char *psz_filename = NULL;
2305 HDC hDC = GetDC( NULL );
2306 EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
2307 ReleaseDC(NULL, hDC);
2309 if( psz_filename == NULL )
2312 /* FIXME: increase i_idx, when concatenated strings */
2317 if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
2323 #ifdef HAVE_FONTCONFIG
2324 static void FontConfig_BuildCache( filter_t *p_filter )
2327 msg_Dbg( p_filter, "Building font databases.");
2332 dialog_progress_bar_t *p_dialog = NULL;
2333 FcConfig *fcConfig = FcInitLoadConfig();
2335 p_dialog = dialog_ProgressCreate( p_filter,
2336 _("Building font cache"),
2337 _("Please wait while your font cache is rebuilt.\n"
2338 "This should take less than a few minutes."), NULL );
2341 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
2343 FcConfigBuildFonts( fcConfig );
2346 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
2347 dialog_ProgressDestroy( p_dialog );
2352 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
2356 * \brief Selects a font matching family, bold, italic provided
2358 static char* FontConfig_Select( FcConfig* config, const char* family,
2359 bool b_bold, bool b_italic, int i_size, int *i_idx )
2361 FcResult result = FcResultMatch;
2362 FcPattern *pat, *p_pat;
2366 /* Create a pattern and fills it */
2367 pat = FcPatternCreate();
2368 if (!pat) return NULL;
2371 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2372 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2373 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2374 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2378 if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
2379 FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
2383 FcDefaultSubstitute( pat );
2384 if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
2386 FcPatternDestroy( pat );
2390 /* Find the best font for the pattern, destroy the pattern */
2391 p_pat = FcFontMatch( config, pat, &result );
2392 FcPatternDestroy( pat );
2393 if( !p_pat || result == FcResultNoMatch ) return NULL;
2395 /* Check the new pattern */
2396 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2397 || ( val_b != FcTrue ) )
2399 FcPatternDestroy( p_pat );
2402 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2407 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2409 FcPatternDestroy( p_pat );
2413 /* if( strcasecmp((const char*)val_s, family ) != 0 )
2414 msg_Warn( p_filter, "fontconfig: selected font family is not"
2415 "the requested one: '%s' != '%s'\n",
2416 (const char*)val_s, family ); */
2418 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2420 FcPatternDestroy( p_pat );
2424 FcPatternDestroy( p_pat );
2425 return strdup( (const char*)val_s );
2428 #else /* from now no styles */
2430 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2431 uint32_t **psz_text_out, uint32_t *pi_runs,
2432 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2433 ft_style_t *p_style )
2435 VLC_UNUSED(p_filter);
2436 VLC_UNUSED(psz_text_in);
2437 VLC_UNUSED(psz_text_out);
2438 VLC_UNUSED(pi_runs);
2439 VLC_UNUSED(ppi_run_lengths);
2440 VLC_UNUSED(ppp_styles);
2441 VLC_UNUSED(p_style);
2444 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2445 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2446 bool b_uline, bool b_through )
2449 VLC_UNUSED(p_fonts);
2451 VLC_UNUSED(b_italic);
2452 VLC_UNUSED(b_uline);
2453 VLC_UNUSED(b_through);
2458 static void FreeLine( line_desc_t *p_line )
2461 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2463 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2465 free( p_line->pp_glyphs );
2466 free( p_line->p_glyph_pos );
2467 free( p_line->p_fg_rgb );
2468 free( p_line->p_bg_rgb );
2469 free( p_line->p_fg_bg_ratio );
2470 free( p_line->pi_underline_offset );
2471 free( p_line->pi_underline_thickness );
2475 static void FreeLines( line_desc_t *p_lines )
2477 line_desc_t *p_line, *p_next;
2479 if( !p_lines ) return;
2481 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2483 p_next = p_line->p_next;
2488 static line_desc_t *NewLine( int i_count )
2490 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2492 if( !p_line ) return NULL;
2493 p_line->i_height = 0;
2494 p_line->i_width = 0;
2495 p_line->p_next = NULL;
2497 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2498 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2499 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2500 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2501 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2502 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2503 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2504 if( ( p_line->pp_glyphs == NULL ) ||
2505 ( p_line->p_glyph_pos == NULL ) ||
2506 ( p_line->p_fg_rgb == NULL ) ||
2507 ( p_line->p_bg_rgb == NULL ) ||
2508 ( p_line->p_fg_bg_ratio == NULL ) ||
2509 ( p_line->pi_underline_offset == NULL ) ||
2510 ( p_line->pi_underline_thickness == NULL ) )
2512 free( p_line->pi_underline_thickness );
2513 free( p_line->pi_underline_offset );
2514 free( p_line->p_fg_rgb );
2515 free( p_line->p_bg_rgb );
2516 free( p_line->p_fg_bg_ratio );
2517 free( p_line->p_glyph_pos );
2518 free( p_line->pp_glyphs );
2522 p_line->pp_glyphs[0] = NULL;
2523 p_line->b_new_color_mode = false;
2528 static int GetFontSize( filter_t *p_filter )
2530 filter_sys_t *p_sys = p_filter->p_sys;
2534 if( p_sys->i_default_font_size )
2536 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2537 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2539 i_size = p_sys->i_default_font_size;
2543 var_Get( p_filter, "freetype-rel-fontsize", &val );
2546 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2547 p_filter->p_sys->i_display_height =
2548 p_filter->fmt_out.video.i_height;
2553 msg_Warn( p_filter, "invalid fontsize, using 12" );
2554 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2555 i_size = 12 * val.i_int / 1000;
2562 static int SetFontSize( filter_t *p_filter, int i_size )
2564 filter_sys_t *p_sys = p_filter->p_sys;
2568 i_size = GetFontSize( p_filter );
2570 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2573 p_sys->i_font_size = i_size;
2575 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2577 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2578 return VLC_EGENERIC;
2584 static void YUVFromRGB( uint32_t i_argb,
2585 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2587 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2588 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2589 int i_blue = ( i_argb & 0x000000ff );
2591 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2592 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2593 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2594 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2595 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2596 -585 * i_blue + 4096 + 1048576) >> 13, 240);