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>
10 * Jean-Baptiste Kempf <jb@videolan.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_stream.h> /* stream_MemoryNew */
38 #include <vlc_input.h> /* vlc_input_attachment_* */
39 #include <vlc_xml.h> /* xml_reader */
40 #include <vlc_strings.h> /* resolve_xml_special_chars */
41 #include <vlc_charset.h> /* ToCharset */
42 #include <vlc_dialog.h> /* FcCache dialog */
46 # define DEFAULT_FONT_FILE "/Library/Fonts/Arial Black.ttf"
47 # define DEFAULT_FAMILY "Arial Black"
48 #elif defined( WIN32 )
49 # define DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
50 # define DEFAULT_FAMILY "Arial"
51 #elif defined( HAVE_MAEMO )
52 # define DEFAULT_FONT_FILE "/usr/share/fonts/nokia/nosnb.ttf"
53 # define DEFAULT_FAMILY "Nokia Sans Bold"
55 # define DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
56 # define DEFAULT_FAMILY "Serif Bold"
60 #include <freetype/ftsynth.h>
61 #include FT_FREETYPE_H
63 #define FT_FLOOR(X) ((X & -64) >> 6)
64 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
66 #define FT_MulFix(v, s) (((v)*(s))>>16)
70 #if defined(HAVE_FRIBIDI)
71 # include <fribidi/fribidi.h>
76 # define _WIN32_IE 0x0500
80 # undef HAVE_FONTCONFIG
84 #ifdef HAVE_FONTCONFIG
85 # include <fontconfig/fontconfig.h>
91 /*****************************************************************************
93 *****************************************************************************/
94 static int Create ( vlc_object_t * );
95 static void Destroy( vlc_object_t * );
97 #define FONT_TEXT N_("Font")
99 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
100 #define FONT_LONGTEXT N_("Font file for the font you want to use")
102 #define FONTSIZE_TEXT N_("Font size in pixels")
103 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
104 "that will be rendered on the video. " \
105 "If set to something different than 0 this option will override the " \
106 "relative font size." )
107 #define OPACITY_TEXT N_("Opacity")
108 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
109 "text that will be rendered on the video. 0 = transparent, " \
110 "255 = totally opaque. " )
111 #define COLOR_TEXT N_("Text default color")
112 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
113 "the video. This must be an hexadecimal (like HTML colors). The first two "\
114 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
115 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
116 #define FONTSIZER_TEXT N_("Relative font size")
117 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
118 "fonts that will be rendered on the video. If absolute font size is set, "\
119 "relative size will be overridden." )
121 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
122 static const char *const ppsz_sizes_text[] = {
123 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
124 #define YUVP_TEXT N_("Use YUVP renderer")
125 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
126 "This option is only needed if you want to encode into DVB subtitles" )
127 #define EFFECT_TEXT N_("Font Effect")
128 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
129 "text to improve its readability." )
131 enum { EFFECT_BACKGROUND = 1,
133 EFFECT_OUTLINE_FAT = 3,
135 static int const pi_effects[] = { EFFECT_BACKGROUND, EFFECT_OUTLINE, EFFECT_OUTLINE_FAT };
136 static const char *const ppsz_effects_text[] = {
137 N_("Background"),N_("Outline"), N_("Fat Outline") };
139 static const int pi_color_values[] = {
140 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
141 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
142 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
144 static const char *const ppsz_color_descriptions[] = {
145 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
146 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
147 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
150 set_shortname( N_("Text renderer"))
151 set_description( N_("Freetype2 font renderer") )
152 set_category( CAT_VIDEO )
153 set_subcategory( SUBCAT_VIDEO_SUBPIC )
156 add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
158 add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
161 add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
162 FONTSIZE_LONGTEXT, true )
165 /* opacity valid on 0..255, with default 255 = fully opaque */
166 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
167 OPACITY_TEXT, OPACITY_LONGTEXT, false )
170 /* hook to the color values list, with default 0x00ffffff = white */
171 add_integer( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
172 COLOR_LONGTEXT, false )
173 change_integer_list( pi_color_values, ppsz_color_descriptions )
176 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
177 FONTSIZER_LONGTEXT, false )
178 change_integer_list( pi_sizes, ppsz_sizes_text )
181 add_integer( "freetype-effect", 2, EFFECT_TEXT,
182 EFFECT_LONGTEXT, false )
183 change_integer_list( pi_effects, ppsz_effects_text )
186 add_bool( "freetype-yuvp", false, YUVP_TEXT,
187 YUVP_LONGTEXT, true )
188 set_capability( "text renderer", 100 )
189 add_shortcut( "text" )
190 set_callbacks( Create, Destroy )
194 /*****************************************************************************
196 *****************************************************************************/
198 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
199 static int RenderText( filter_t *, subpicture_region_t *,
200 subpicture_region_t * );
203 static int RenderHtml( filter_t *, subpicture_region_t *,
204 subpicture_region_t * );
206 #ifdef HAVE_FONTCONFIG
207 static void FontConfig_BuildCache( filter_t * );
208 static char* FontConfig_Select( FcConfig *, const char *,
209 bool, bool, int, int * );
212 static char* Win32_Select( filter_t *, const char *,
213 bool, bool, int, int * );
216 static int LoadFontsFromAttachments( filter_t *p_filter );
218 static int GetFontSize( filter_t *p_filter );
219 static int SetFontSize( filter_t *, int );
220 static void YUVFromRGB( uint32_t i_argb,
221 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
223 typedef struct line_desc_t line_desc_t;
226 /** NULL-terminated list of glyphs making the string */
227 FT_BitmapGlyph *pp_glyphs;
228 /** list of relative positions for the glyphs */
229 FT_Vector *p_glyph_pos;
230 /** list of RGB information for styled text
231 * -- if the rendering mode supports it (RenderYUVA) and
232 * b_new_color_mode is set, then it becomes possible to
233 * have multicoloured text within the subtitles. */
236 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
237 bool b_new_color_mode;
238 /** underline information -- only supplied if text should be underlined */
239 int *pi_underline_offset;
240 uint16_t *pi_underline_thickness;
244 int i_red, i_green, i_blue;
249 static line_desc_t *NewLine( int );
254 uint32_t i_font_color; /* ARGB */
255 uint32_t i_karaoke_bg_color; /* ARGB */
263 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
264 static void FreeLines( line_desc_t * );
265 static void FreeLine( line_desc_t * );
267 /*****************************************************************************
268 * filter_sys_t: freetype local data
269 *****************************************************************************
270 * This structure is part of the video output thread descriptor.
271 * It describes the freetype specific properties of an output thread.
272 *****************************************************************************/
275 FT_Library p_library; /* handle to library */
276 FT_Face p_face; /* handle to face object */
278 uint8_t i_font_opacity;
283 int i_default_font_size;
284 int i_display_height;
286 char* psz_fontfamily;
289 char* psz_win_fonts_path;
293 input_attachment_t **pp_font_attachments;
294 int i_font_attachments;
297 #define UCHAR uint32_t
298 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
299 #define TR_FONT_STYLE_PTR ft_style_t *
301 #include "text_renderer.h"
303 /*****************************************************************************
304 * Create: allocates osd-text video thread output method
305 *****************************************************************************
306 * This function allocates and initializes a Clone vout method.
307 *****************************************************************************/
308 static int Create( vlc_object_t *p_this )
310 filter_t *p_filter = (filter_t *)p_this;
312 char *psz_fontfile = NULL;
313 char *psz_fontfamily = NULL;
314 int i_error = 0, fontindex = 0;
316 /* Allocate structure */
317 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
322 p_sys->psz_fontfamily = NULL;
326 p_sys->p_library = 0;
327 p_sys->i_font_size = 0;
328 p_sys->i_display_height = 0;
330 var_Create( p_filter, "freetype-rel-fontsize",
331 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
333 psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
334 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
335 p_sys->i_effect = var_InheritInteger( p_filter, "freetype-effect" );
336 p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
337 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
338 p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
339 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
342 /* Get Windows Font folder */
343 wchar_t wdir[MAX_PATH];
344 if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
346 GetWindowsDirectoryW( wdir, MAX_PATH );
347 wcscat( wdir, L"\\fonts" );
349 p_sys->psz_win_fonts_path = FromWide( wdir );
352 /* Set default psz_fontfamily */
353 if( !psz_fontfamily || !*psz_fontfamily )
355 free( psz_fontfamily );
357 psz_fontfamily = strdup( DEFAULT_FAMILY );
360 if( asprintf( &psz_fontfamily, "%s%s", p_sys->psz_win_fonts_path, DEFAULT_FONT_FILE ) == -1 )
363 psz_fontfamily = strdup( DEFAULT_FONT_FILE );
365 msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
369 /* Set the current font file */
371 p_sys->psz_fontfamily = psz_fontfamily;
372 #ifdef HAVE_FONTCONFIG
373 FontConfig_BuildCache( p_filter );
376 psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
377 p_sys->i_default_font_size, &fontindex );
379 psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
380 p_sys->i_default_font_size, &fontindex );
383 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
385 /* If nothing is found, use the default family */
387 psz_fontfile = strdup( psz_fontfamily );
389 #else /* !HAVE_STYLES */
390 /* Use the default file */
391 psz_fontfile = psz_fontfamily;
395 i_error = FT_Init_FreeType( &p_sys->p_library );
398 msg_Err( p_filter, "couldn't initialize freetype" );
402 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
403 fontindex, &p_sys->p_face );
405 if( i_error == FT_Err_Unknown_File_Format )
407 msg_Err( p_filter, "file %s have unknown format",
408 psz_fontfile ? psz_fontfile : "(null)" );
413 msg_Err( p_filter, "failed to load font file %s",
414 psz_fontfile ? psz_fontfile : "(null)" );
418 free( psz_fontfile );
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 );
451 free( psz_fontfile );
453 free( psz_fontfamily );
458 /*****************************************************************************
459 * Destroy: destroy Clone video thread output method
460 *****************************************************************************
461 * Clean up all data and library connections
462 *****************************************************************************/
463 static void Destroy( vlc_object_t *p_this )
465 filter_t *p_filter = (filter_t *)p_this;
466 filter_sys_t *p_sys = p_filter->p_sys;
468 if( p_sys->pp_font_attachments )
470 for( int k = 0; k < p_sys->i_font_attachments; k++ )
471 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
473 free( p_sys->pp_font_attachments );
477 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
478 free( p_sys->psz_fontfamily );
481 /* FcFini asserts calling the subfunction FcCacheFini()
482 * even if no other library functions have been made since FcInit(),
483 * so don't call it. */
485 FT_Done_Face( p_sys->p_face );
486 FT_Done_FreeType( p_sys->p_library );
490 /*****************************************************************************
491 * Make any TTF/OTF fonts present in the attachments of the media file
492 * and store them for later use by the FreeType Engine
493 *****************************************************************************/
494 static int LoadFontsFromAttachments( filter_t *p_filter )
496 filter_sys_t *p_sys = p_filter->p_sys;
497 input_attachment_t **pp_attachments;
498 int i_attachments_cnt;
500 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
503 p_sys->i_font_attachments = 0;
504 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
505 if( !p_sys->pp_font_attachments )
508 for( int k = 0; k < i_attachments_cnt; k++ )
510 input_attachment_t *p_attach = pp_attachments[k];
512 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
513 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
514 p_attach->i_data > 0 && p_attach->p_data )
516 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
520 vlc_input_attachment_Delete( p_attach );
523 free( pp_attachments );
528 /*****************************************************************************
529 * Render: place string in picture
530 *****************************************************************************
531 * This function merges the previously rendered freetype glyphs into a picture
532 *****************************************************************************/
533 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
534 line_desc_t *p_line, int i_width, int i_height )
536 VLC_UNUSED(p_filter);
537 static const uint8_t pi_gamma[16] =
538 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
539 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
543 int i, x, y, i_pitch;
544 uint8_t i_y; /* YUV values, derived from incoming RGB */
547 /* Create a new subpicture region */
548 memset( &fmt, 0, sizeof(video_format_t) );
549 fmt.i_chroma = VLC_CODEC_YUVP;
550 fmt.i_width = fmt.i_visible_width = i_width + 4;
551 fmt.i_height = fmt.i_visible_height = i_height + 4;
552 if( p_region->fmt.i_visible_width > 0 )
553 fmt.i_visible_width = p_region->fmt.i_visible_width;
554 if( p_region->fmt.i_visible_height > 0 )
555 fmt.i_visible_height = p_region->fmt.i_visible_height;
556 fmt.i_x_offset = fmt.i_y_offset = 0;
560 assert( !p_region->p_picture );
561 p_region->p_picture = picture_NewFromFormat( &fmt );
562 if( !p_region->p_picture )
564 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
567 /* Calculate text color components */
568 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
569 25 * p_line->i_blue + 128) >> 8) + 16;
570 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
571 112 * p_line->i_blue + 128) >> 8) + 128;
572 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
573 18 * p_line->i_blue + 128) >> 8) + 128;
576 fmt.p_palette->i_entries = 16;
577 for( i = 0; i < 8; i++ )
579 fmt.p_palette->palette[i][0] = 0;
580 fmt.p_palette->palette[i][1] = 0x80;
581 fmt.p_palette->palette[i][2] = 0x80;
582 fmt.p_palette->palette[i][3] = pi_gamma[i];
583 fmt.p_palette->palette[i][3] =
584 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
586 for( i = 8; i < fmt.p_palette->i_entries; i++ )
588 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
589 fmt.p_palette->palette[i][1] = i_u;
590 fmt.p_palette->palette[i][2] = i_v;
591 fmt.p_palette->palette[i][3] = pi_gamma[i];
592 fmt.p_palette->palette[i][3] =
593 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
596 p_dst = p_region->p_picture->Y_PIXELS;
597 i_pitch = p_region->p_picture->Y_PITCH;
599 /* Initialize the region pixels */
600 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
602 for( ; p_line != NULL; p_line = p_line->p_next )
604 int i_glyph_tmax = 0;
605 int i_bitmap_offset, i_offset, i_align_offset = 0;
606 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
608 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
609 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
612 if( p_line->i_width < i_width )
614 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
616 i_align_offset = i_width - p_line->i_width;
618 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
620 i_align_offset = ( i_width - p_line->i_width ) / 2;
624 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
626 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
628 i_offset = ( p_line->p_glyph_pos[ i ].y +
629 i_glyph_tmax - p_glyph->top + 2 ) *
630 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
633 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
635 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
637 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
639 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
646 /* Outlining (find something better than nearest neighbour filtering ?) */
649 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
650 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
651 uint8_t left, current;
653 for( y = 1; y < (int)fmt.i_height - 1; y++ )
655 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
656 p_dst += p_region->p_picture->Y_PITCH;
659 for( x = 1; x < (int)fmt.i_width - 1; x++ )
662 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
663 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;
667 memset( p_top, 0, fmt.i_width );
673 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
674 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
675 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
676 int i_glyph_tmax, int i_align_offset,
677 uint8_t i_y, uint8_t i_u, uint8_t i_v,
678 subpicture_region_t *p_region)
681 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
683 p_dst_y = p_region->p_picture->Y_PIXELS;
684 p_dst_u = p_region->p_picture->U_PIXELS;
685 p_dst_v = p_region->p_picture->V_PIXELS;
686 p_dst_a = p_region->p_picture->A_PIXELS;
687 i_pitch = p_region->p_picture->A_PITCH;
689 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
690 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
692 for( int y = 0; y < i_line_thickness; y++ )
694 int i_extra = p_this_glyph->bitmap.width;
698 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
699 (p_this_glyph_pos->x + p_this_glyph->left);
701 for( int x = 0; x < i_extra; x++ )
705 /* break the underline around the tails of any glyphs which cross it */
706 /* Strikethrough doesn't get broken */
707 for( int z = x - i_line_thickness;
708 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
711 if( p_next_glyph && ( z >= i_extra ) )
713 int i_row = i_line_offset + p_next_glyph->top + y;
715 if( ( p_next_glyph->bitmap.rows > i_row ) &&
716 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
721 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
723 int i_row = i_line_offset + p_this_glyph->top + y;
725 if( ( p_this_glyph->bitmap.rows > i_row ) &&
726 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
735 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
736 p_dst_u[i_offset+x] = i_u;
737 p_dst_v[i_offset+x] = i_v;
738 p_dst_a[i_offset+x] = 255;
745 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
747 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
748 int i_pitch = p_region->p_picture->A_PITCH;
751 for( ; p_line != NULL; p_line = p_line->p_next )
753 int i_glyph_tmax=0, i = 0;
754 int i_bitmap_offset, i_offset, i_align_offset = 0;
755 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
757 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
758 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
761 if( p_line->i_width < i_width )
763 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
765 i_align_offset = i_width - p_line->i_width;
767 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
769 i_align_offset = ( i_width - p_line->i_width ) / 2;
773 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
775 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
777 i_offset = ( p_line->p_glyph_pos[ i ].y +
778 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
779 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
780 i_align_offset +xoffset;
782 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
784 for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
786 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
787 if( p_dst[i_offset+x] <
788 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
790 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
798 /*****************************************************************************
799 * Render: place string in picture
800 *****************************************************************************
801 * This function merges the previously rendered freetype glyphs into a picture
802 *****************************************************************************/
803 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
804 line_desc_t *p_line, int i_width, int i_height )
806 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
808 int i, y, i_pitch, i_alpha;
809 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
811 if( i_width == 0 || i_height == 0 )
814 /* Create a new subpicture region */
815 memset( &fmt, 0, sizeof(video_format_t) );
816 fmt.i_chroma = VLC_CODEC_YUVA;
817 fmt.i_width = fmt.i_visible_width = i_width + 6;
818 fmt.i_height = fmt.i_visible_height = i_height + 6;
819 if( p_region->fmt.i_visible_width > 0 )
820 fmt.i_visible_width = p_region->fmt.i_visible_width;
821 if( p_region->fmt.i_visible_height > 0 )
822 fmt.i_visible_height = p_region->fmt.i_visible_height;
823 fmt.i_x_offset = fmt.i_y_offset = 0;
827 p_region->p_picture = picture_NewFromFormat( &fmt );
828 if( !p_region->p_picture )
832 /* Calculate text color components */
833 YUVFromRGB( (p_line->i_red << 16) |
834 (p_line->i_green << 8) |
837 i_alpha = p_line->i_alpha;
839 p_dst_y = p_region->p_picture->Y_PIXELS;
840 p_dst_u = p_region->p_picture->U_PIXELS;
841 p_dst_v = p_region->p_picture->V_PIXELS;
842 p_dst_a = p_region->p_picture->A_PIXELS;
843 i_pitch = p_region->p_picture->A_PITCH;
845 /* Initialize the region pixels */
846 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
848 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
849 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
850 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
851 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
855 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
856 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
857 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
858 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
860 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
861 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
863 DrawBlack( p_line, i_width, p_region, 0, 0);
864 DrawBlack( p_line, i_width, p_region, -1, 0);
865 DrawBlack( p_line, i_width, p_region, 0, -1);
866 DrawBlack( p_line, i_width, p_region, 1, 0);
867 DrawBlack( p_line, i_width, p_region, 0, 1);
870 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
872 DrawBlack( p_line, i_width, p_region, -1, -1);
873 DrawBlack( p_line, i_width, p_region, -1, 1);
874 DrawBlack( p_line, i_width, p_region, 1, -1);
875 DrawBlack( p_line, i_width, p_region, 1, 1);
877 DrawBlack( p_line, i_width, p_region, -2, 0);
878 DrawBlack( p_line, i_width, p_region, 0, -2);
879 DrawBlack( p_line, i_width, p_region, 2, 0);
880 DrawBlack( p_line, i_width, p_region, 0, 2);
882 DrawBlack( p_line, i_width, p_region, -2, -2);
883 DrawBlack( p_line, i_width, p_region, -2, 2);
884 DrawBlack( p_line, i_width, p_region, 2, -2);
885 DrawBlack( p_line, i_width, p_region, 2, 2);
887 DrawBlack( p_line, i_width, p_region, -3, 0);
888 DrawBlack( p_line, i_width, p_region, 0, -3);
889 DrawBlack( p_line, i_width, p_region, 3, 0);
890 DrawBlack( p_line, i_width, p_region, 0, 3);
893 for( ; p_line != NULL; p_line = p_line->p_next )
895 int i_glyph_tmax = 0;
896 int i_bitmap_offset, i_offset, i_align_offset = 0;
897 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
899 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
900 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
903 if( p_line->i_width < i_width )
905 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
907 i_align_offset = i_width - p_line->i_width;
909 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
911 i_align_offset = ( i_width - p_line->i_width ) / 2;
915 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
917 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
919 i_offset = ( p_line->p_glyph_pos[ i ].y +
920 i_glyph_tmax - p_glyph->top + 3 ) *
921 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
924 if( p_line->b_new_color_mode )
926 /* Every glyph can (and in fact must) have its own color */
927 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
930 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
932 for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
934 uint8_t i_y_local = i_y;
935 uint8_t i_u_local = i_u;
936 uint8_t i_v_local = i_v;
938 if( p_line->p_fg_bg_ratio != 0x00 )
940 int i_split = p_glyph->bitmap.width *
941 p_line->p_fg_bg_ratio[ i ] / 0x7f;
945 YUVFromRGB( p_line->p_bg_rgb[ i ],
946 &i_y_local, &i_u_local, &i_v_local );
950 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
952 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
953 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
955 p_dst_u[i_offset+x] = i_u;
956 p_dst_v[i_offset+x] = i_v;
958 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
959 p_dst_a[i_offset+x] = 0xff;
965 if( p_line->pi_underline_thickness[ i ] )
967 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
968 p_line->pi_underline_offset[ i ],
969 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
970 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
971 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
972 i_glyph_tmax, i_align_offset,
979 /* Apply the alpha setting */
980 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
981 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
987 * This function renders a text subpicture region into another one.
988 * It also calculates the size needed for this string, and renders the
989 * needed glyphs into memory. It is used as pf_add_string callback in
990 * the vout method by this module
992 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
993 subpicture_region_t *p_region_in )
995 filter_sys_t *p_sys = p_filter->p_sys;
996 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
997 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
998 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
999 size_t i_string_length;
1001 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1011 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1012 psz_string = p_region_in->psz_text;
1013 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1015 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1016 i_scale = val.i_int;
1018 if( p_region_in->p_style )
1020 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1021 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1022 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1026 i_font_color = p_sys->i_font_color;
1027 i_font_alpha = 255 - p_sys->i_font_opacity;
1028 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1031 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1032 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1033 SetFontSize( p_filter, i_font_size );
1035 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1036 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1037 i_blue = i_font_color & 0x000000FF;
1039 result.x = result.y = 0;
1040 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1042 #if defined(WORDS_BIGENDIAN)
1043 psz_unicode = ToCharset( "UCS-4BE", psz_string, &i_string_length );
1045 psz_unicode = ToCharset( "UCS-4LE", psz_string, &i_string_length );
1047 if( psz_unicode == NULL )
1049 psz_unicode_orig = psz_unicode;
1050 i_string_length /= 4;
1052 #if defined(HAVE_FRIBIDI)
1054 uint32_t *p_fribidi_string;
1058 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1059 if( !p_fribidi_string )
1062 /* Do bidi conversion line-by-line */
1063 while( pos < i_string_length )
1065 while( pos < i_string_length )
1067 i_char = psz_unicode[pos];
1068 if (i_char != '\r' && i_char != '\n')
1070 p_fribidi_string[pos] = i_char;
1074 while( pos < i_string_length )
1076 i_char = psz_unicode[pos];
1077 if (i_char == '\r' || i_char == '\n')
1081 if (pos > start_pos)
1083 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1084 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1086 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1088 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1091 (FriBidiChar*)p_fribidi_string + start_pos,
1096 free( psz_unicode_orig );
1097 psz_unicode = psz_unicode_orig = p_fribidi_string;
1098 p_fribidi_string[ i_string_length ] = 0;
1102 /* Calculate relative glyph positions and a bounding box for the
1104 if( !(p_line = NewLine( strlen( psz_string ))) )
1107 i_pen_x = i_pen_y = 0;
1109 psz_line_start = psz_unicode;
1111 #define face p_sys->p_face
1112 #define glyph face->glyph
1114 while( *psz_unicode )
1116 i_char = *psz_unicode++;
1117 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1122 if( i_char == '\n' )
1124 psz_line_start = psz_unicode;
1125 if( !(p_next = NewLine( strlen( psz_string ))) )
1127 p_line->p_next = p_next;
1128 p_line->i_width = line.xMax;
1129 p_line->i_height = face->size->metrics.height >> 6;
1130 p_line->pp_glyphs[ i ] = NULL;
1131 p_line->i_alpha = i_font_alpha;
1132 p_line->i_red = i_red;
1133 p_line->i_green = i_green;
1134 p_line->i_blue = i_blue;
1137 result.x = __MAX( result.x, line.xMax );
1138 result.y += face->size->metrics.height >> 6;
1141 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1142 i_pen_y += face->size->metrics.height >> 6;
1144 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1149 i_glyph_index = FT_Get_Char_Index( face, i_char );
1150 if( p_sys->i_use_kerning && i_glyph_index
1154 FT_Get_Kerning( face, i_previous, i_glyph_index,
1155 ft_kerning_default, &delta );
1156 i_pen_x += delta.x >> 6;
1159 p_line->p_glyph_pos[ i ].x = i_pen_x;
1160 p_line->p_glyph_pos[ i ].y = i_pen_y;
1161 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1164 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1167 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1172 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1175 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1179 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1180 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1183 FT_Done_Glyph( tmp_glyph );
1186 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1189 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1190 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1191 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1193 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1194 p_line->pp_glyphs[ i ] = NULL;
1196 p_line = NewLine( strlen( psz_string ));
1197 if( p_prev ) p_prev->p_next = p_line;
1198 else p_lines = p_line;
1200 uint32_t *psz_unicode_saved = psz_unicode;
1201 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1205 if( psz_unicode == psz_line_start )
1206 { /* try harder to break that line */
1207 psz_unicode = psz_unicode_saved;
1208 while( psz_unicode > psz_line_start &&
1209 *psz_unicode != '_' && *psz_unicode != '/' &&
1210 *psz_unicode != '\\' && *psz_unicode != '.' )
1215 if( psz_unicode == psz_line_start )
1217 msg_Warn( p_filter, "unbreakable string" );
1222 *psz_unicode = '\n';
1224 psz_unicode = psz_line_start;
1227 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1230 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1231 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1233 i_previous = i_glyph_index;
1234 i_pen_x += glyph->advance.x >> 6;
1238 p_line->i_width = line.xMax;
1239 p_line->i_height = face->size->metrics.height >> 6;
1240 p_line->pp_glyphs[ i ] = NULL;
1241 p_line->i_alpha = i_font_alpha;
1242 p_line->i_red = i_red;
1243 p_line->i_green = i_green;
1244 p_line->i_blue = i_blue;
1245 result.x = __MAX( result.x, line.xMax );
1246 result.y += line.yMax - line.yMin;
1251 p_region_out->i_x = p_region_in->i_x;
1252 p_region_out->i_y = p_region_in->i_y;
1254 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1255 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1257 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1259 free( psz_unicode_orig );
1260 FreeLines( p_lines );
1264 free( psz_unicode_orig );
1265 FreeLines( p_lines );
1266 return VLC_EGENERIC;
1270 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1271 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1272 bool b_italic, bool b_uline, bool b_through )
1274 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1278 p_style->i_font_size = i_font_size;
1279 p_style->i_font_color = i_font_color;
1280 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1281 p_style->b_italic = b_italic;
1282 p_style->b_bold = b_bold;
1283 p_style->b_underline = b_uline;
1284 p_style->b_through = b_through;
1286 p_style->psz_fontname = strdup( psz_fontname );
1291 static void DeleteStyle( ft_style_t *p_style )
1295 free( p_style->psz_fontname );
1300 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1307 if(( s1->i_font_size == s2->i_font_size ) &&
1308 ( s1->i_font_color == s2->i_font_color ) &&
1309 ( s1->b_italic == s2->b_italic ) &&
1310 ( s1->b_through == s2->b_through ) &&
1311 ( s1->b_bold == s2->b_bold ) &&
1312 ( s1->b_underline == s2->b_underline ) &&
1313 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1320 static void IconvText( filter_t *p_filter, const char *psz_string,
1321 size_t *i_string_length, uint32_t **ppsz_unicode )
1323 *i_string_length = 0;
1324 if( *ppsz_unicode == NULL )
1329 #if defined(WORDS_BIGENDIAN)
1330 ToCharset( "UCS-4BE", psz_string, &i_length );
1332 ToCharset( "UCS-4LE", psz_string, &i_length );
1336 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1339 memcpy( *ppsz_unicode, psz_tmp, i_length );
1340 *i_string_length = i_length / 4;
1345 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1346 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1347 bool b_uline, bool b_through )
1349 ft_style_t *p_style = NULL;
1351 char *psz_fontname = NULL;
1352 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1353 uint32_t i_karaoke_bg_color = i_font_color;
1354 int i_font_size = p_sys->i_font_size;
1356 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1357 &i_font_color, &i_karaoke_bg_color ))
1359 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1360 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1365 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1366 bool b_uline, bool b_through, bool b_bold,
1367 bool b_italic, int i_karaoke_bgcolor,
1368 line_desc_t *p_line, uint32_t *psz_unicode,
1369 int *pi_pen_x, int i_pen_y, int *pi_start,
1370 FT_Vector *p_result )
1375 bool b_first_on_line = true;
1378 int i_pen_x_start = *pi_pen_x;
1380 uint32_t *psz_unicode_start = psz_unicode;
1382 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1384 /* Account for part of line already in position */
1385 for( i = 0; i<*pi_start; i++ )
1389 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1390 ft_glyph_bbox_pixels, &glyph_size );
1392 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1393 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1394 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1395 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1401 b_first_on_line = false;
1403 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1409 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1410 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1414 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1415 ft_kerning_default, &delta );
1416 *pi_pen_x += delta.x >> 6;
1418 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1419 p_line->p_glyph_pos[ i ].y = i_pen_y;
1421 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1424 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1428 "unable to render text FT_Load_Glyph returned %d", i_error );
1429 p_line->pp_glyphs[ i ] = NULL;
1430 return VLC_EGENERIC;
1434 /* Do synthetic styling now that Freetype supports it;
1435 * ie. if the font we have loaded is NOT already in the
1436 * style that the tags want, then switch it on; if they
1437 * are then don't. */
1438 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1439 FT_GlyphSlot_Embolden( p_face->glyph );
1440 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1441 FT_GlyphSlot_Oblique( p_face->glyph );
1443 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1447 "unable to render text FT_Get_Glyph returned %d", i_error );
1448 p_line->pp_glyphs[ i ] = NULL;
1449 return VLC_EGENERIC;
1451 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1452 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1455 FT_Done_Glyph( tmp_glyph );
1458 if( b_uline || b_through )
1460 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1461 p_face->size->metrics.y_scale));
1462 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1463 p_face->size->metrics.y_scale));
1465 p_line->pi_underline_offset[ i ] =
1466 ( aOffset < 0 ) ? -aOffset : aOffset;
1467 p_line->pi_underline_thickness[ i ] =
1468 ( aSize < 0 ) ? -aSize : aSize;
1471 /* Move the baseline to make it strikethrough instead of
1472 * underline. That means that strikethrough takes precedence
1474 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1475 p_face->size->metrics.y_scale));
1477 p_line->pi_underline_offset[ i ] -=
1478 ( aDescent < 0 ) ? -aDescent : aDescent;
1482 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1483 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1484 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1485 p_line->p_fg_bg_ratio[ i ] = 0x00;
1487 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1488 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1489 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1491 for( ; i >= *pi_start; i-- )
1492 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1495 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1499 if( psz_unicode == psz_unicode_start )
1501 if( b_first_on_line )
1503 msg_Warn( p_filter, "unbreakable string" );
1504 p_line->pp_glyphs[ i ] = NULL;
1505 return VLC_EGENERIC;
1507 *pi_pen_x = i_pen_x_start;
1509 p_line->i_width = line.xMax;
1510 p_line->i_height = __MAX( p_line->i_height,
1511 p_face->size->metrics.height >> 6 );
1512 p_line->pp_glyphs[ i ] = NULL;
1514 p_result->x = __MAX( p_result->x, line.xMax );
1515 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1516 i_yMax - i_yMin ) );
1521 *psz_unicode = '\n';
1523 psz_unicode = psz_unicode_start;
1524 *pi_pen_x = i_pen_x_start;
1532 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1533 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1535 i_previous = i_glyph_index;
1536 *pi_pen_x += p_face->glyph->advance.x >> 6;
1539 p_line->i_width = line.xMax;
1540 p_line->i_height = __MAX( p_line->i_height,
1541 p_face->size->metrics.height >> 6 );
1542 p_line->pp_glyphs[ i ] = NULL;
1544 p_result->x = __MAX( p_result->x, line.xMax );
1545 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1546 line.yMax - line.yMin ) );
1550 /* Get rid of any text processed - if necessary repositioning
1551 * at the start of a new line of text
1555 *psz_unicode_start = '\0';
1557 else if( psz_unicode > psz_unicode_start )
1559 for( i=0; psz_unicode[ i ]; i++ )
1560 psz_unicode_start[ i ] = psz_unicode[ i ];
1561 psz_unicode_start[ i ] = '\0';
1567 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1568 uint32_t **psz_text_out, uint32_t *pi_runs,
1569 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1570 ft_style_t *p_style )
1572 size_t i_string_length;
1574 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1575 *psz_text_out += i_string_length;
1577 if( ppp_styles && ppi_run_lengths )
1581 /* XXX this logic looks somewhat broken */
1585 *ppp_styles = realloc_or_free( *ppp_styles,
1586 *pi_runs * sizeof( ft_style_t * ) );
1588 else if( *pi_runs == 1 )
1590 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1593 /* We have just malloc'ed this memory successfully -
1594 * *pi_runs HAS to be within the memory area of *ppp_styles */
1597 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1601 /* XXX more iffy logic */
1603 if( *ppi_run_lengths )
1605 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1606 *pi_runs * sizeof( uint32_t ) );
1608 else if( *pi_runs == 1 )
1610 *ppi_run_lengths = (uint32_t *)
1611 malloc( *pi_runs * sizeof( uint32_t ) );
1614 /* same remarks here */
1615 if( *ppi_run_lengths )
1617 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1620 /* If we couldn't use the p_style argument due to memory allocation
1621 * problems above, release it here.
1623 if( p_style ) DeleteStyle( p_style );
1626 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1628 for( int k = 0; k < p_sys->i_font_attachments; k++ )
1630 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1632 FT_Face p_face = NULL;
1634 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1642 bool match = !strcasecmp( p_face->family_name,
1643 p_style->psz_fontname );
1645 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1646 match = match && p_style->b_bold;
1648 match = match && !p_style->b_bold;
1650 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1651 match = match && p_style->b_italic;
1653 match = match && !p_style->b_italic;
1661 FT_Done_Face( p_face );
1666 return VLC_EGENERIC;
1669 static int ProcessLines( filter_t *p_filter,
1674 uint32_t *pi_run_lengths,
1675 ft_style_t **pp_styles,
1676 line_desc_t **pp_lines,
1678 FT_Vector *p_result,
1682 uint32_t *pi_k_run_lengths,
1683 uint32_t *pi_k_durations )
1685 filter_sys_t *p_sys = p_filter->p_sys;
1686 ft_style_t **pp_char_styles;
1687 int *p_new_positions = NULL;
1688 int8_t *p_levels = NULL;
1689 uint8_t *pi_karaoke_bar = NULL;
1693 /* Assign each character in the text string its style explicitly, so that
1694 * after the characters have been shuffled around by Fribidi, we can re-apply
1695 * the styles, and to simplify the calculation of runs within a line.
1697 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1698 if( !pp_char_styles )
1703 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1704 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1705 * we just won't be able to display the progress bar; at least we'll
1711 for( j = 0; j < i_runs; j++ )
1712 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1713 pp_char_styles[ i++ ] = pp_styles[ j ];
1715 #if defined(HAVE_FRIBIDI)
1717 ft_style_t **pp_char_styles_new;
1718 int *p_old_positions;
1719 uint32_t *p_fribidi_string;
1720 int start_pos, pos = 0;
1722 pp_char_styles_new = (ft_style_t **)
1723 malloc( i_len * sizeof( ft_style_t * ));
1725 p_fribidi_string = (uint32_t *)
1726 malloc( (i_len + 1) * sizeof(uint32_t) );
1727 p_old_positions = (int *)
1728 malloc( (i_len + 1) * sizeof( int ) );
1729 p_new_positions = (int *)
1730 malloc( (i_len + 1) * sizeof( int ) );
1731 p_levels = (int8_t *)
1732 malloc( (i_len + 1) * sizeof( int8_t ) );
1734 if( ! pp_char_styles_new ||
1735 ! p_fribidi_string ||
1736 ! p_old_positions ||
1737 ! p_new_positions ||
1741 free( p_old_positions );
1742 free( p_new_positions );
1743 free( p_fribidi_string );
1744 free( pp_char_styles_new );
1745 free( pi_karaoke_bar );
1747 free( pp_char_styles );
1751 /* Do bidi conversion line-by-line */
1754 while(pos < i_len) {
1755 if (psz_text[pos] != '\n')
1757 p_fribidi_string[pos] = psz_text[pos];
1758 pp_char_styles_new[pos] = pp_char_styles[pos];
1759 p_new_positions[pos] = pos;
1764 while(pos < i_len) {
1765 if (psz_text[pos] == '\n')
1769 if (pos > start_pos)
1771 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1772 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1774 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1776 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1777 pos - start_pos, &base_dir,
1778 (FriBidiChar*)p_fribidi_string + start_pos,
1779 p_new_positions + start_pos,
1781 p_levels + start_pos );
1782 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1784 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1785 p_old_positions[ j - start_pos ] ];
1786 p_new_positions[ j ] += start_pos;
1790 free( p_old_positions );
1791 free( pp_char_styles );
1792 pp_char_styles = pp_char_styles_new;
1793 psz_text = p_fribidi_string;
1794 p_fribidi_string[ i_len ] = 0;
1797 /* Work out the karaoke */
1798 if( pi_karaoke_bar )
1800 int64_t i_last_duration = 0;
1801 int64_t i_duration = 0;
1802 int64_t i_start_pos = 0;
1803 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1805 for( k = 0; k< i_k_runs; k++ )
1807 double fraction = 0.0;
1809 i_duration += pi_k_durations[ k ];
1811 if( i_duration < i_elapsed )
1813 /* Completely finished this run-length -
1814 * let it render normally */
1818 else if( i_elapsed < i_last_duration )
1820 /* Haven't got up to this segment yet -
1821 * render it completely in karaoke BG mode */
1827 /* Partway through this run */
1829 fraction = (double)(i_elapsed - i_last_duration) /
1830 (double)pi_k_durations[ k ];
1832 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1834 double shade = pi_k_run_lengths[ k ] * fraction;
1836 if( p_new_positions )
1837 j = p_new_positions[ i_start_pos + i ];
1839 j = i_start_pos + i;
1841 if( i < (uint32_t)shade )
1842 pi_karaoke_bar[ j ] = 0xff;
1843 else if( (double)i > shade )
1844 pi_karaoke_bar[ j ] = 0x00;
1847 shade -= (int)shade;
1848 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1849 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1853 i_last_duration = i_duration;
1854 i_start_pos += pi_k_run_lengths[ k ];
1858 free( p_new_positions );
1860 FT_Vector tmp_result;
1862 line_desc_t *p_line = NULL;
1863 line_desc_t *p_prev = NULL;
1869 p_result->x = p_result->y = 0;
1870 tmp_result.x = tmp_result.y = 0;
1873 for( k = 0; k <= (uint32_t) i_len; k++ )
1875 if( ( k == (uint32_t) i_len ) ||
1877 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1879 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1881 /* End of the current style run */
1882 FT_Face p_face = NULL;
1885 /* Look for a match amongst our attachments first */
1886 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1890 char *psz_fontfile = NULL;
1892 #ifdef HAVE_FONTCONFIG
1893 psz_fontfile = FontConfig_Select( NULL,
1894 p_style->psz_fontname,
1899 #elif defined( WIN32 )
1900 psz_fontfile = Win32_Select( p_filter,
1901 p_style->psz_fontname,
1909 if( psz_fontfile && ! *psz_fontfile )
1911 msg_Warn( p_filter, "We were not able to find a matching font: \"%s\" %s,"
1912 " so using default font", p_style->psz_fontname,
1913 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1914 (p_style->b_bold ? "(Bold)" :
1915 (p_style->b_italic ? "(Italic)" : ""))) );
1916 free( psz_fontfile );
1917 psz_fontfile = NULL;
1922 if( FT_New_Face( p_sys->p_library,
1923 psz_fontfile, i_idx, &p_face ) )
1925 free( psz_fontfile );
1926 free( pp_char_styles );
1927 #if defined(HAVE_FRIBIDI)
1930 free( pi_karaoke_bar );
1931 return VLC_EGENERIC;
1933 free( psz_fontfile );
1937 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1939 /* We've loaded a font face which is unhelpful for actually
1940 * rendering text - fallback to the default one.
1942 FT_Done_Face( p_face );
1946 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1947 ft_encoding_unicode ) ||
1948 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1949 p_style->i_font_size ) )
1951 if( p_face ) FT_Done_Face( p_face );
1952 free( pp_char_styles );
1953 #if defined(HAVE_FRIBIDI)
1956 free( pi_karaoke_bar );
1957 return VLC_EGENERIC;
1959 p_sys->i_use_kerning =
1960 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1963 uint32_t *psz_unicode = (uint32_t *)
1964 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1967 if( p_face ) FT_Done_Face( p_face );
1968 free( pp_char_styles );
1969 free( psz_unicode );
1970 #if defined(HAVE_FRIBIDI)
1973 free( pi_karaoke_bar );
1976 memcpy( psz_unicode, psz_text + i_prev,
1977 sizeof( uint32_t ) * ( k - i_prev ) );
1978 psz_unicode[ k - i_prev ] = 0;
1979 while( *psz_unicode )
1983 if( !(p_line = NewLine( i_len - i_prev)) )
1985 if( p_face ) FT_Done_Face( p_face );
1986 free( pp_char_styles );
1987 free( psz_unicode );
1988 #if defined(HAVE_FRIBIDI)
1991 free( pi_karaoke_bar );
1994 /* New Color mode only works in YUVA rendering mode --
1995 * (RGB mode has palette constraints on it). We therefore
1996 * need to populate the legacy colour fields also.
1998 p_line->b_new_color_mode = true;
1999 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2000 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2001 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2002 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2003 p_line->p_next = NULL;
2005 i_pen_y += tmp_result.y;
2009 if( p_prev ) p_prev->p_next = p_line;
2010 else *pp_lines = p_line;
2013 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2014 p_style->i_font_color, p_style->b_underline,
2018 p_style->i_karaoke_bg_color,
2019 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2020 &tmp_result ) != VLC_SUCCESS )
2022 if( p_face ) FT_Done_Face( p_face );
2023 free( pp_char_styles );
2024 free( psz_unicode );
2025 #if defined(HAVE_FRIBIDI)
2028 free( pi_karaoke_bar );
2029 return VLC_EGENERIC;
2034 p_result->x = __MAX( p_result->x, tmp_result.x );
2035 p_result->y += tmp_result.y;
2040 if( *psz_unicode == '\n')
2044 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2046 *c_ptr = *(c_ptr+1);
2051 free( psz_unicode );
2052 if( p_face ) FT_Done_Face( p_face );
2056 free( pp_char_styles );
2057 #if defined(HAVE_FRIBIDI)
2062 p_result->x = __MAX( p_result->x, tmp_result.x );
2063 p_result->y += tmp_result.y;
2066 if( pi_karaoke_bar )
2069 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2071 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2073 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2077 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2079 /* 100% BG colour will render faster if we
2080 * instead make it 100% FG colour, so leave
2081 * the ratio alone and copy the value across
2083 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2087 if( pi_karaoke_bar[ i ] & 0x80 )
2089 /* Swap Left and Right sides over for Right aligned
2090 * language text (eg. Arabic, Hebrew)
2092 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2094 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2095 p_line->p_bg_rgb[ k ] = i_tmp;
2097 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2100 /* Jump over the '\n' at the line-end */
2103 free( pi_karaoke_bar );
2109 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2110 subpicture_region_t *p_region_in )
2112 int rv = VLC_SUCCESS;
2113 stream_t *p_sub = NULL;
2115 if( !p_region_in || !p_region_in->psz_html )
2116 return VLC_EGENERIC;
2118 /* Reset the default fontsize in case screen metrics have changed */
2119 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2121 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2122 (uint8_t *) p_region_in->psz_html,
2123 strlen( p_region_in->psz_html ),
2125 if( unlikely(p_sub == NULL) )
2128 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2129 bool b_karaoke = false;
2132 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2134 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2136 p_filter->p_sys->p_xml = p_xml_reader;
2139 /* Look for Root Node */
2142 if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2144 if( !strcasecmp( "karaoke", node ) )
2146 /* We're going to have to render the text a number
2147 * of times to show the progress marker on the text.
2149 var_SetBool( p_filter, "text-rerender", true );
2152 else if( !strcasecmp( "text", node ) )
2158 /* Only text and karaoke tags are supported */
2159 msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2166 msg_Err( p_filter, "Malformed HTML subtitle" );
2170 if( rv != VLC_SUCCESS )
2172 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2173 p_xml_reader = NULL;
2181 uint32_t i_runs = 0;
2182 uint32_t i_k_runs = 0;
2183 uint32_t *pi_run_lengths = NULL;
2184 uint32_t *pi_k_run_lengths = NULL;
2185 uint32_t *pi_k_durations = NULL;
2186 ft_style_t **pp_styles = NULL;
2187 FT_Vector result = {0, 0};
2188 line_desc_t *p_lines = NULL;
2190 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2191 sizeof( uint32_t ) );
2194 rv = ProcessNodes( p_filter, p_xml_reader,
2195 p_region_in->p_style, psz_text, &i_len,
2196 &i_runs, &pi_run_lengths, &pp_styles,
2197 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2200 p_region_out->i_x = p_region_in->i_x;
2201 p_region_out->i_y = p_region_in->i_y;
2203 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2205 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2206 pi_run_lengths, pp_styles, &p_lines,
2207 &result, b_karaoke, i_k_runs,
2208 pi_k_run_lengths, pi_k_durations );
2211 for( uint_fast32_t k=0; k<i_runs; k++)
2212 DeleteStyle( pp_styles[k] );
2214 free( pi_run_lengths );
2217 /* Don't attempt to render text that couldn't be layed out
2219 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2221 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2222 Render( p_filter, p_region_out, p_lines,
2223 result.x, result.y );
2225 RenderYUVA( p_filter, p_region_out, p_lines,
2226 result.x, result.y );
2229 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2230 FreeLines( p_lines );
2232 stream_Delete( p_sub );
2238 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
2240 static int GetFileFontByName( const char *font_name, char **psz_filename )
2243 wchar_t vbuffer[MAX_PATH];
2244 wchar_t dbuffer[256];
2246 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey) != ERROR_SUCCESS )
2249 for( int index = 0;; index++ )
2251 DWORD vbuflen = MAX_PATH - 1;
2252 DWORD dbuflen = 255;
2254 if( RegEnumValueW( hKey, index, vbuffer, &vbuflen,
2255 NULL, NULL, (LPBYTE)dbuffer, &dbuflen) != ERROR_SUCCESS )
2258 char *psz_value = FromWide( vbuffer );
2260 char *s = strchr( psz_value,'(' );
2261 if( s != NULL && s != psz_value ) s[-1] = '\0';
2263 /* Manage concatenated font names */
2264 if( strchr( psz_value, '&') ) {
2265 if( strcasestr( psz_value, font_name ) != NULL )
2269 if( strcasecmp( psz_value, font_name ) == 0 )
2274 *psz_filename = FromWide( dbuffer );
2279 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
2280 DWORD type, LPARAM lParam)
2282 VLC_UNUSED( metric );
2283 if( (type & RASTER_FONTTYPE) ) return 1;
2284 // if( lpelfe->elfScript ) FIXME
2286 return GetFileFontByName( (const char *)lpelfe->elfFullName, (char **)lParam );
2289 static char* Win32_Select( filter_t *p_filter, const char* family,
2290 bool b_bold, bool b_italic, int i_size, int *i_idx )
2292 VLC_UNUSED( i_size );
2293 // msg_Dbg( p_filter, "Here in Win32_Select, asking for %s", family );
2297 lf.lfCharSet = DEFAULT_CHARSET;
2301 lf.lfWeight = FW_BOLD;
2302 strncpy( (LPSTR)&lf.lfFaceName, family, 32);
2305 char *psz_filename = NULL;
2306 HDC hDC = GetDC( NULL );
2307 EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
2308 ReleaseDC(NULL, hDC);
2310 if( psz_filename == NULL )
2313 /* FIXME: increase i_idx, when concatenated strings */
2318 if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
2324 #ifdef HAVE_FONTCONFIG
2325 static void FontConfig_BuildCache( filter_t *p_filter )
2328 msg_Dbg( p_filter, "Building font databases.");
2333 dialog_progress_bar_t *p_dialog = NULL;
2334 FcConfig *fcConfig = FcInitLoadConfig();
2336 p_dialog = dialog_ProgressCreate( p_filter,
2337 _("Building font cache"),
2338 _("Please wait while your font cache is rebuilt.\n"
2339 "This should take less than a few minutes."), NULL );
2342 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
2344 FcConfigBuildFonts( fcConfig );
2347 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
2348 dialog_ProgressDestroy( p_dialog );
2353 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
2357 * \brief Selects a font matching family, bold, italic provided
2359 static char* FontConfig_Select( FcConfig* config, const char* family,
2360 bool b_bold, bool b_italic, int i_size, int *i_idx )
2362 FcResult result = FcResultMatch;
2363 FcPattern *pat, *p_pat;
2367 /* Create a pattern and fills it */
2368 pat = FcPatternCreate();
2369 if (!pat) return NULL;
2372 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2373 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2374 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2375 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2379 if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
2381 FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
2382 free( psz_fontsize );
2387 FcDefaultSubstitute( pat );
2388 if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
2390 FcPatternDestroy( pat );
2394 /* Find the best font for the pattern, destroy the pattern */
2395 p_pat = FcFontMatch( config, pat, &result );
2396 FcPatternDestroy( pat );
2397 if( !p_pat || result == FcResultNoMatch ) return NULL;
2399 /* Check the new pattern */
2400 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2401 || ( val_b != FcTrue ) )
2403 FcPatternDestroy( p_pat );
2406 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2411 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2413 FcPatternDestroy( p_pat );
2417 /* if( strcasecmp((const char*)val_s, family ) != 0 )
2418 msg_Warn( p_filter, "fontconfig: selected font family is not"
2419 "the requested one: '%s' != '%s'\n",
2420 (const char*)val_s, family ); */
2422 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2424 FcPatternDestroy( p_pat );
2428 FcPatternDestroy( p_pat );
2429 return strdup( (const char*)val_s );
2432 #else /* from now no styles */
2434 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2435 uint32_t **psz_text_out, uint32_t *pi_runs,
2436 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2437 ft_style_t *p_style )
2439 VLC_UNUSED(p_filter);
2440 VLC_UNUSED(psz_text_in);
2441 VLC_UNUSED(psz_text_out);
2442 VLC_UNUSED(pi_runs);
2443 VLC_UNUSED(ppi_run_lengths);
2444 VLC_UNUSED(ppp_styles);
2445 VLC_UNUSED(p_style);
2448 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2449 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2450 bool b_uline, bool b_through )
2453 VLC_UNUSED(p_fonts);
2455 VLC_UNUSED(b_italic);
2456 VLC_UNUSED(b_uline);
2457 VLC_UNUSED(b_through);
2462 static void FreeLine( line_desc_t *p_line )
2465 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2467 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2469 free( p_line->pp_glyphs );
2470 free( p_line->p_glyph_pos );
2471 free( p_line->p_fg_rgb );
2472 free( p_line->p_bg_rgb );
2473 free( p_line->p_fg_bg_ratio );
2474 free( p_line->pi_underline_offset );
2475 free( p_line->pi_underline_thickness );
2479 static void FreeLines( line_desc_t *p_lines )
2481 line_desc_t *p_line, *p_next;
2483 if( !p_lines ) return;
2485 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2487 p_next = p_line->p_next;
2492 static line_desc_t *NewLine( int i_count )
2494 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2496 if( !p_line ) return NULL;
2497 p_line->i_height = 0;
2498 p_line->i_width = 0;
2499 p_line->p_next = NULL;
2501 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2502 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2503 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2504 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2505 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2506 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2507 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2508 if( ( p_line->pp_glyphs == NULL ) ||
2509 ( p_line->p_glyph_pos == NULL ) ||
2510 ( p_line->p_fg_rgb == NULL ) ||
2511 ( p_line->p_bg_rgb == NULL ) ||
2512 ( p_line->p_fg_bg_ratio == NULL ) ||
2513 ( p_line->pi_underline_offset == NULL ) ||
2514 ( p_line->pi_underline_thickness == NULL ) )
2516 free( p_line->pi_underline_thickness );
2517 free( p_line->pi_underline_offset );
2518 free( p_line->p_fg_rgb );
2519 free( p_line->p_bg_rgb );
2520 free( p_line->p_fg_bg_ratio );
2521 free( p_line->p_glyph_pos );
2522 free( p_line->pp_glyphs );
2526 p_line->pp_glyphs[0] = NULL;
2527 p_line->b_new_color_mode = false;
2532 static int GetFontSize( filter_t *p_filter )
2534 filter_sys_t *p_sys = p_filter->p_sys;
2538 if( p_sys->i_default_font_size )
2540 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2541 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2543 i_size = p_sys->i_default_font_size;
2547 var_Get( p_filter, "freetype-rel-fontsize", &val );
2550 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2551 p_filter->p_sys->i_display_height =
2552 p_filter->fmt_out.video.i_height;
2557 msg_Warn( p_filter, "invalid fontsize, using 12" );
2558 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2559 i_size = 12 * val.i_int / 1000;
2566 static int SetFontSize( filter_t *p_filter, int i_size )
2568 filter_sys_t *p_sys = p_filter->p_sys;
2572 i_size = GetFontSize( p_filter );
2574 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2577 p_sys->i_font_size = i_size;
2579 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2581 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2582 return VLC_EGENERIC;
2588 static void YUVFromRGB( uint32_t i_argb,
2589 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2591 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2592 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2593 int i_blue = ( i_argb & 0x000000ff );
2595 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2596 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2597 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2598 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2599 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2600 -585 * i_blue + 4096 + 1048576) >> 13, 240);