1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <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
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
42 #include <vlc_dialog.h>
43 #include <vlc_memory.h>
49 #include FT_FREETYPE_H
51 #define FT_FLOOR(X) ((X & -64) >> 6)
52 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
54 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
59 #define FC_DEFAULT_FONT "Arial Black"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
66 #elif defined( HAVE_MAEMO )
67 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
68 #define FC_DEFAULT_FONT "Nokia Sans Bold"
70 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
71 #define FC_DEFAULT_FONT "Serif Bold"
74 #if defined(HAVE_FRIBIDI)
75 #include <fribidi/fribidi.h>
78 #ifdef HAVE_FONTCONFIG
79 #include <fontconfig/fontconfig.h>
81 #define DEFAULT_FONT FC_DEFAULT_FONT
86 /*****************************************************************************
88 *****************************************************************************/
89 static int Create ( vlc_object_t * );
90 static void Destroy( vlc_object_t * );
92 #define FONT_TEXT N_("Font")
94 #ifdef HAVE_FONTCONFIG
95 #define FONT_LONGTEXT N_("Font family for the font you want to use")
97 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
100 #define FONTSIZE_TEXT N_("Font size in pixels")
101 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
102 "that will be rendered on the video. " \
103 "If set to something different than 0 this option will override the " \
104 "relative font size." )
105 #define OPACITY_TEXT N_("Opacity")
106 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
107 "text that will be rendered on the video. 0 = transparent, " \
108 "255 = totally opaque. " )
109 #define COLOR_TEXT N_("Text default color")
110 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
111 "the video. This must be an hexadecimal (like HTML colors). The first two "\
112 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
113 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
114 #define FONTSIZER_TEXT N_("Relative font size")
115 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
116 "fonts that will be rendered on the video. If absolute font size is set, "\
117 "relative size will be overriden." )
119 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
120 static const char *const ppsz_sizes_text[] = {
121 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
122 #define YUVP_TEXT N_("Use YUVP renderer")
123 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
124 "This option is only needed if you want to encode into DVB subtitles" )
125 #define EFFECT_TEXT N_("Font Effect")
126 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
127 "text to improve its readability." )
129 #define EFFECT_BACKGROUND 1
130 #define EFFECT_OUTLINE 2
131 #define EFFECT_OUTLINE_FAT 3
133 static int const pi_effects[] = { 1, 2, 3 };
134 static const char *const ppsz_effects_text[] = {
135 N_("Background"),N_("Outline"), N_("Fat Outline") };
136 static const int pi_color_values[] = {
137 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
138 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
139 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
141 static const char *const ppsz_color_descriptions[] = {
142 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
143 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
144 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
147 set_shortname( N_("Text renderer"))
148 set_description( N_("Freetype2 font renderer") )
149 set_category( CAT_VIDEO )
150 set_subcategory( SUBCAT_VIDEO_SUBPIC )
152 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
155 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
156 FONTSIZE_LONGTEXT, true )
158 /* opacity valid on 0..255, with default 255 = fully opaque */
159 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
160 OPACITY_TEXT, OPACITY_LONGTEXT, true )
162 /* hook to the color values list, with default 0x00ffffff = white */
163 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
164 COLOR_LONGTEXT, false )
165 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
167 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
168 FONTSIZER_LONGTEXT, false )
169 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
170 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
171 EFFECT_LONGTEXT, false )
172 change_integer_list( pi_effects, ppsz_effects_text, NULL )
174 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
175 YUVP_LONGTEXT, true )
176 set_capability( "text renderer", 100 )
177 add_shortcut( "text" )
178 set_callbacks( Create, Destroy )
183 /*****************************************************************************
185 *****************************************************************************/
187 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
188 static int RenderText( filter_t *, subpicture_region_t *,
189 subpicture_region_t * );
190 #ifdef HAVE_FONTCONFIG
191 static int RenderHtml( filter_t *, subpicture_region_t *,
192 subpicture_region_t * );
193 static char *FontConfig_Select( FcConfig *, const char *,
198 static int LoadFontsFromAttachments( filter_t *p_filter );
200 static int GetFontSize( filter_t *p_filter );
201 static int SetFontSize( filter_t *, int );
202 static void YUVFromRGB( uint32_t i_argb,
203 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
205 typedef struct line_desc_t line_desc_t;
208 /** NULL-terminated list of glyphs making the string */
209 FT_BitmapGlyph *pp_glyphs;
210 /** list of relative positions for the glyphs */
211 FT_Vector *p_glyph_pos;
212 /** list of RGB information for styled text
213 * -- if the rendering mode supports it (RenderYUVA) and
214 * b_new_color_mode is set, then it becomes possible to
215 * have multicoloured text within the subtitles. */
218 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
219 bool b_new_color_mode;
220 /** underline information -- only supplied if text should be underlined */
221 uint16_t *pi_underline_offset;
222 uint16_t *pi_underline_thickness;
226 int i_red, i_green, i_blue;
231 static line_desc_t *NewLine( int );
236 uint32_t i_font_color; /* ARGB */
237 uint32_t i_karaoke_bg_color; /* ARGB */
244 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
245 static void FreeLines( line_desc_t * );
246 static void FreeLine( line_desc_t * );
248 /*****************************************************************************
249 * filter_sys_t: freetype local data
250 *****************************************************************************
251 * This structure is part of the video output thread descriptor.
252 * It describes the freetype specific properties of an output thread.
253 *****************************************************************************/
256 FT_Library p_library; /* handle to library */
257 FT_Face p_face; /* handle to face object */
259 uint8_t i_font_opacity;
264 int i_default_font_size;
265 int i_display_height;
266 #ifdef HAVE_FONTCONFIG
267 char* psz_fontfamily;
271 input_attachment_t **pp_font_attachments;
272 int i_font_attachments;
276 #define UCHAR uint32_t
277 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
278 #define TR_FONT_STYLE_PTR ft_style_t *
280 #include "text_renderer.h"
282 /*****************************************************************************
283 * Create: allocates osd-text video thread output method
284 *****************************************************************************
285 * This function allocates and initializes a Clone vout method.
286 *****************************************************************************/
287 static int Create( vlc_object_t *p_this )
289 filter_t *p_filter = (filter_t *)p_this;
291 char *psz_fontfile=NULL;
292 char *psz_fontfamily=NULL;
293 int i_error,fontindex;
295 #ifdef HAVE_FONTCONFIG
296 FcPattern *fontpattern = NULL, *fontmatch = NULL;
297 /* Initialise result to Match, as fontconfig doesnt
298 * really set this other than some error-cases */
299 FcResult fontresult = FcResultMatch;
303 /* Allocate structure */
304 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
307 #ifdef HAVE_FONTCONFIG
308 p_sys->psz_fontfamily = NULL;
312 p_sys->p_library = 0;
313 p_sys->i_font_size = 0;
314 p_sys->i_display_height = 0;
316 var_Create( p_filter, "freetype-rel-fontsize",
317 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
319 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
320 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
321 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
322 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
323 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
324 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
325 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
328 if( !psz_fontfamily || !*psz_fontfamily )
330 free( psz_fontfamily );
331 #ifdef HAVE_FONTCONFIG
332 psz_fontfamily=strdup( DEFAULT_FONT );
334 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
335 if( !psz_fontfamily )
338 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
339 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
341 strcpy( psz_fontfamily, DEFAULT_FONT );
343 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
347 #ifdef HAVE_FONTCONFIG
348 /* Lets find some fontfile from freetype-font variable family */
350 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
354 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
355 _("Building font cache"),
356 _("Please wait while your font cache is rebuilt.\n"
357 "This should take less than few minutes."), NULL );
358 char *path = xmalloc( PATH_MAX + 1 );
359 /* Fontconfig doesnt seem to know where windows fonts are with
360 * current contribs. So just tell default windows font directory
361 * is the place to search fonts
363 GetWindowsDirectory( path, PATH_MAX + 1 );
364 strcat( path, "\\fonts" );
366 dialog_ProgressSet( p_dialog, NULL, 0.4 );
368 FcConfigAppFontAddDir( NULL , path );
373 dialog_ProgressSet( p_dialog, NULL, 0.5 );
377 msg_Dbg( p_filter, "Building font database.");
379 FcConfigBuildFonts( NULL );
382 msg_Dbg( p_filter, "Finished building font database." );
383 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
385 fontpattern = FcPatternCreate();
389 msg_Err( p_filter, "Creating fontpattern failed");
395 dialog_ProgressSet( p_dialog, NULL, 0.7 );
397 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
398 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
399 free( psz_fontsize );
401 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
403 msg_Err( p_filter, "FontSubstitute failed");
406 FcDefaultSubstitute( fontpattern );
410 dialog_ProgressSet( p_dialog, NULL, 0.8 );
412 /* testing fontresult here doesn't do any good really, but maybe it will
413 * in future as fontconfig code doesn't set it in all cases and just
414 * returns NULL or doesn't set to to Match on all Match cases.*/
415 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
416 if( !fontmatch || fontresult == FcResultNoMatch )
418 msg_Err( p_filter, "Fontmatching failed");
422 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
423 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
426 msg_Err( p_filter, "Failed to get fontfile");
430 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
431 psz_fontfile ? psz_fontfile : "(null)" );
432 p_sys->psz_fontfamily = strdup( psz_fontfamily );
436 dialog_ProgressSet( p_dialog, NULL, 1.0 );
437 dialog_ProgressDestroy( p_dialog );
443 #ifdef HAVE_FONTCONFIG
444 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
445 psz_fontfile = psz_fontfamily;
450 i_error = FT_Init_FreeType( &p_sys->p_library );
453 msg_Err( p_filter, "couldn't initialize freetype" );
457 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
458 fontindex, &p_sys->p_face );
460 if( i_error == FT_Err_Unknown_File_Format )
462 msg_Err( p_filter, "file %s have unknown format",
463 psz_fontfile ? psz_fontfile : "(null)" );
468 msg_Err( p_filter, "failed to load font file %s",
469 psz_fontfile ? psz_fontfile : "(null)" );
473 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
476 msg_Err( p_filter, "font has no unicode translation table" );
480 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
482 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
485 p_sys->pp_font_attachments = NULL;
486 p_sys->i_font_attachments = 0;
488 p_filter->pf_render_text = RenderText;
489 #ifdef HAVE_FONTCONFIG
490 p_filter->pf_render_html = RenderHtml;
491 FcPatternDestroy( fontmatch );
492 FcPatternDestroy( fontpattern );
494 p_filter->pf_render_html = NULL;
497 free( psz_fontfamily );
498 LoadFontsFromAttachments( p_filter );
503 #ifdef HAVE_FONTCONFIG
504 if( fontmatch ) FcPatternDestroy( fontmatch );
505 if( fontpattern ) FcPatternDestroy( fontpattern );
507 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
508 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
509 free( psz_fontfamily );
514 /*****************************************************************************
515 * Destroy: destroy Clone video thread output method
516 *****************************************************************************
517 * Clean up all data and library connections
518 *****************************************************************************/
519 static void Destroy( vlc_object_t *p_this )
521 filter_t *p_filter = (filter_t *)p_this;
522 filter_sys_t *p_sys = p_filter->p_sys;
524 if( p_sys->pp_font_attachments )
528 for( k = 0; k < p_sys->i_font_attachments; k++ )
529 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
531 free( p_sys->pp_font_attachments );
534 #ifdef HAVE_FONTCONFIG
535 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
536 free( p_sys->psz_fontfamily );
539 /* FcFini asserts calling the subfunction FcCacheFini()
540 * even if no other library functions have been made since FcInit(),
541 * so don't call it. */
543 FT_Done_Face( p_sys->p_face );
544 FT_Done_FreeType( p_sys->p_library );
548 /*****************************************************************************
549 * Make any TTF/OTF fonts present in the attachments of the media file
550 * and store them for later use by the FreeType Engine
551 *****************************************************************************/
552 static int LoadFontsFromAttachments( filter_t *p_filter )
554 filter_sys_t *p_sys = p_filter->p_sys;
555 input_thread_t *p_input;
556 input_attachment_t **pp_attachments;
557 int i_attachments_cnt;
559 int rv = VLC_SUCCESS;
561 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
565 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
567 vlc_object_release(p_input);
571 p_sys->i_font_attachments = 0;
572 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
573 if(! p_sys->pp_font_attachments )
576 for( k = 0; k < i_attachments_cnt; k++ )
578 input_attachment_t *p_attach = pp_attachments[k];
580 if( p_sys->pp_font_attachments )
582 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
583 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
584 ( p_attach->i_data > 0 ) &&
585 ( p_attach->p_data != NULL ) )
587 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
591 vlc_input_attachment_Delete( p_attach );
596 vlc_input_attachment_Delete( p_attach );
599 free( pp_attachments );
601 vlc_object_release(p_input);
606 /*****************************************************************************
607 * Render: place string in picture
608 *****************************************************************************
609 * This function merges the previously rendered freetype glyphs into a picture
610 *****************************************************************************/
611 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
612 line_desc_t *p_line, int i_width, int i_height )
614 VLC_UNUSED(p_filter);
615 static const uint8_t pi_gamma[16] =
616 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
617 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
621 int i, x, y, i_pitch;
622 uint8_t i_y; /* YUV values, derived from incoming RGB */
625 /* Create a new subpicture region */
626 memset( &fmt, 0, sizeof(video_format_t) );
627 fmt.i_chroma = VLC_CODEC_YUVP;
628 fmt.i_width = fmt.i_visible_width = i_width + 4;
629 fmt.i_height = fmt.i_visible_height = i_height + 4;
630 if( p_region->fmt.i_visible_width > 0 )
631 fmt.i_visible_width = p_region->fmt.i_visible_width;
632 if( p_region->fmt.i_visible_height > 0 )
633 fmt.i_visible_height = p_region->fmt.i_visible_height;
634 fmt.i_x_offset = fmt.i_y_offset = 0;
636 assert( !p_region->p_picture );
637 p_region->p_picture = picture_NewFromFormat( &fmt );
638 if( !p_region->p_picture )
642 /* Calculate text color components */
643 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
644 25 * p_line->i_blue + 128) >> 8) + 16;
645 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
646 112 * p_line->i_blue + 128) >> 8) + 128;
647 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
648 18 * p_line->i_blue + 128) >> 8) + 128;
651 fmt.p_palette->i_entries = 16;
652 for( i = 0; i < 8; i++ )
654 fmt.p_palette->palette[i][0] = 0;
655 fmt.p_palette->palette[i][1] = 0x80;
656 fmt.p_palette->palette[i][2] = 0x80;
657 fmt.p_palette->palette[i][3] = pi_gamma[i];
658 fmt.p_palette->palette[i][3] =
659 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
661 for( i = 8; i < fmt.p_palette->i_entries; i++ )
663 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
664 fmt.p_palette->palette[i][1] = i_u;
665 fmt.p_palette->palette[i][2] = i_v;
666 fmt.p_palette->palette[i][3] = pi_gamma[i];
667 fmt.p_palette->palette[i][3] =
668 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
671 p_dst = p_region->p_picture->Y_PIXELS;
672 i_pitch = p_region->p_picture->Y_PITCH;
674 /* Initialize the region pixels */
675 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
677 for( ; p_line != NULL; p_line = p_line->p_next )
679 int i_glyph_tmax = 0;
680 int i_bitmap_offset, i_offset, i_align_offset = 0;
681 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
683 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
684 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
687 if( p_line->i_width < i_width )
689 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
691 i_align_offset = i_width - p_line->i_width;
693 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
695 i_align_offset = ( i_width - p_line->i_width ) / 2;
699 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
701 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
703 i_offset = ( p_line->p_glyph_pos[ i ].y +
704 i_glyph_tmax - p_glyph->top + 2 ) *
705 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
708 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
710 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
712 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
714 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
721 /* Outlining (find something better than nearest neighbour filtering ?) */
724 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
725 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
726 uint8_t left, current;
728 for( y = 1; y < (int)fmt.i_height - 1; y++ )
730 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
731 p_dst += p_region->p_picture->Y_PITCH;
734 for( x = 1; x < (int)fmt.i_width - 1; x++ )
737 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
738 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;
742 memset( p_top, 0, fmt.i_width );
748 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
749 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
750 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
751 int i_glyph_tmax, int i_align_offset,
752 uint8_t i_y, uint8_t i_u, uint8_t i_v,
753 subpicture_region_t *p_region)
757 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
759 p_dst_y = p_region->p_picture->Y_PIXELS;
760 p_dst_u = p_region->p_picture->U_PIXELS;
761 p_dst_v = p_region->p_picture->V_PIXELS;
762 p_dst_a = p_region->p_picture->A_PIXELS;
763 i_pitch = p_region->p_picture->A_PITCH;
765 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
766 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
768 for( y = 0; y < i_line_thickness; y++ )
770 int i_extra = p_this_glyph->bitmap.width;
774 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
775 (p_this_glyph_pos->x + p_this_glyph->left);
777 for( x = 0; x < i_extra; x++ )
781 /* break the underline around the tails of any glyphs which cross it */
782 for( z = x - i_line_thickness;
783 z < x + i_line_thickness && b_ok;
786 if( p_next_glyph && ( z >= i_extra ) )
788 int i_row = i_line_offset + p_next_glyph->top + y;
790 if( ( p_next_glyph->bitmap.rows > i_row ) &&
791 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
796 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
798 int i_row = i_line_offset + p_this_glyph->top + y;
800 if( ( p_this_glyph->bitmap.rows > i_row ) &&
801 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
810 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
811 p_dst_u[i_offset+x] = i_u;
812 p_dst_v[i_offset+x] = i_v;
813 p_dst_a[i_offset+x] = 255;
820 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
822 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
823 int i_pitch = p_region->p_picture->A_PITCH;
826 for( ; p_line != NULL; p_line = p_line->p_next )
828 int i_glyph_tmax=0, i = 0;
829 int i_bitmap_offset, i_offset, i_align_offset = 0;
830 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
832 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
833 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
836 if( p_line->i_width < i_width )
838 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
840 i_align_offset = i_width - p_line->i_width;
842 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
844 i_align_offset = ( i_width - p_line->i_width ) / 2;
848 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
850 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
852 i_offset = ( p_line->p_glyph_pos[ i ].y +
853 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
854 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
855 i_align_offset +xoffset;
857 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
859 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
861 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
862 if( p_dst[i_offset+x] <
863 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
865 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
874 /*****************************************************************************
875 * Render: place string in picture
876 *****************************************************************************
877 * This function merges the previously rendered freetype glyphs into a picture
878 *****************************************************************************/
879 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
880 line_desc_t *p_line, int i_width, int i_height )
882 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
884 int i, x, y, i_pitch, i_alpha;
885 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
887 if( i_width == 0 || i_height == 0 )
890 /* Create a new subpicture region */
891 memset( &fmt, 0, sizeof(video_format_t) );
892 fmt.i_chroma = VLC_CODEC_YUVA;
893 fmt.i_width = fmt.i_visible_width = i_width + 6;
894 fmt.i_height = fmt.i_visible_height = i_height + 6;
895 if( p_region->fmt.i_visible_width > 0 )
896 fmt.i_visible_width = p_region->fmt.i_visible_width;
897 if( p_region->fmt.i_visible_height > 0 )
898 fmt.i_visible_height = p_region->fmt.i_visible_height;
899 fmt.i_x_offset = fmt.i_y_offset = 0;
901 p_region->p_picture = picture_NewFromFormat( &fmt );
902 if( !p_region->p_picture )
906 /* Calculate text color components */
907 YUVFromRGB( (p_line->i_red << 16) |
908 (p_line->i_green << 8) |
911 i_alpha = p_line->i_alpha;
913 p_dst_y = p_region->p_picture->Y_PIXELS;
914 p_dst_u = p_region->p_picture->U_PIXELS;
915 p_dst_v = p_region->p_picture->V_PIXELS;
916 p_dst_a = p_region->p_picture->A_PIXELS;
917 i_pitch = p_region->p_picture->A_PITCH;
919 /* Initialize the region pixels */
920 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
922 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
923 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
924 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
925 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
929 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
930 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
931 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
932 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
934 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
935 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
937 DrawBlack( p_line, i_width, p_region, 0, 0);
938 DrawBlack( p_line, i_width, p_region, -1, 0);
939 DrawBlack( p_line, i_width, p_region, 0, -1);
940 DrawBlack( p_line, i_width, p_region, 1, 0);
941 DrawBlack( p_line, i_width, p_region, 0, 1);
944 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
946 DrawBlack( p_line, i_width, p_region, -1, -1);
947 DrawBlack( p_line, i_width, p_region, -1, 1);
948 DrawBlack( p_line, i_width, p_region, 1, -1);
949 DrawBlack( p_line, i_width, p_region, 1, 1);
951 DrawBlack( p_line, i_width, p_region, -2, 0);
952 DrawBlack( p_line, i_width, p_region, 0, -2);
953 DrawBlack( p_line, i_width, p_region, 2, 0);
954 DrawBlack( p_line, i_width, p_region, 0, 2);
956 DrawBlack( p_line, i_width, p_region, -2, -2);
957 DrawBlack( p_line, i_width, p_region, -2, 2);
958 DrawBlack( p_line, i_width, p_region, 2, -2);
959 DrawBlack( p_line, i_width, p_region, 2, 2);
961 DrawBlack( p_line, i_width, p_region, -3, 0);
962 DrawBlack( p_line, i_width, p_region, 0, -3);
963 DrawBlack( p_line, i_width, p_region, 3, 0);
964 DrawBlack( p_line, i_width, p_region, 0, 3);
967 for( ; p_line != NULL; p_line = p_line->p_next )
969 int i_glyph_tmax = 0;
970 int i_bitmap_offset, i_offset, i_align_offset = 0;
971 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
973 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
974 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
977 if( p_line->i_width < i_width )
979 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
981 i_align_offset = i_width - p_line->i_width;
983 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
985 i_align_offset = ( i_width - p_line->i_width ) / 2;
989 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
991 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
993 i_offset = ( p_line->p_glyph_pos[ i ].y +
994 i_glyph_tmax - p_glyph->top + 3 ) *
995 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
998 if( p_line->b_new_color_mode )
1000 /* Every glyph can (and in fact must) have its own color */
1001 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1004 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1006 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1008 uint8_t i_y_local = i_y;
1009 uint8_t i_u_local = i_u;
1010 uint8_t i_v_local = i_v;
1012 if( p_line->p_fg_bg_ratio != 0x00 )
1014 int i_split = p_glyph->bitmap.width *
1015 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1019 YUVFromRGB( p_line->p_bg_rgb[ i ],
1020 &i_y_local, &i_u_local, &i_v_local );
1024 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1026 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1027 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1029 p_dst_u[i_offset+x] = i_u;
1030 p_dst_v[i_offset+x] = i_v;
1032 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1033 p_dst_a[i_offset+x] = 0xff;
1036 i_offset += i_pitch;
1039 if( p_line->pi_underline_thickness[ i ] )
1041 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1042 p_line->pi_underline_offset[ i ],
1043 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1044 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1045 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1046 i_glyph_tmax, i_align_offset,
1053 /* Apply the alpha setting */
1054 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1055 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1061 * This function renders a text subpicture region into another one.
1062 * It also calculates the size needed for this string, and renders the
1063 * needed glyphs into memory. It is used as pf_add_string callback in
1064 * the vout method by this module
1066 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1067 subpicture_region_t *p_region_in )
1069 filter_sys_t *p_sys = p_filter->p_sys;
1070 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1071 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1072 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1073 int i_string_length;
1075 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1076 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1086 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1087 psz_string = p_region_in->psz_text;
1088 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1090 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1091 i_scale = val.i_int;
1093 if( p_region_in->p_style )
1095 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1096 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1097 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1101 i_font_color = p_sys->i_font_color;
1102 i_font_alpha = 255 - p_sys->i_font_opacity;
1103 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1106 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1107 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1108 SetFontSize( p_filter, i_font_size );
1110 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1111 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1112 i_blue = i_font_color & 0x000000FF;
1114 result.x = result.y = 0;
1115 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1117 psz_unicode = psz_unicode_orig =
1118 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1119 if( psz_unicode == NULL )
1121 #if defined(WORDS_BIGENDIAN)
1122 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1124 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1126 if( iconv_handle == (vlc_iconv_t)-1 )
1128 msg_Warn( p_filter, "unable to do conversion" );
1134 const char *p_in_buffer = psz_string;
1135 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1136 i_in_bytes = strlen( psz_string );
1137 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1138 i_out_bytes_left = i_out_bytes;
1139 p_out_buffer = (char *)psz_unicode;
1140 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1142 &p_out_buffer, &i_out_bytes_left );
1144 vlc_iconv_close( iconv_handle );
1148 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1149 "bytes left %u", (unsigned)i_in_bytes );
1152 *(uint32_t*)p_out_buffer = 0;
1153 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1156 #if defined(HAVE_FRIBIDI)
1158 uint32_t *p_fribidi_string;
1159 int32_t start_pos, pos = 0;
1161 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1162 if( !p_fribidi_string )
1165 /* Do bidi conversion line-by-line */
1166 while( pos < i_string_length )
1168 while( pos < i_string_length )
1170 i_char = psz_unicode[pos];
1171 if (i_char != '\r' && i_char != '\n')
1173 p_fribidi_string[pos] = i_char;
1177 while( pos < i_string_length )
1179 i_char = psz_unicode[pos];
1180 if (i_char == '\r' || i_char == '\n')
1184 if (pos > start_pos)
1186 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1187 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1190 (FriBidiChar*)p_fribidi_string + start_pos,
1195 free( psz_unicode_orig );
1196 psz_unicode = psz_unicode_orig = p_fribidi_string;
1197 p_fribidi_string[ i_string_length ] = 0;
1201 /* Calculate relative glyph positions and a bounding box for the
1203 if( !(p_line = NewLine( strlen( psz_string ))) )
1206 i_pen_x = i_pen_y = 0;
1208 psz_line_start = psz_unicode;
1210 #define face p_sys->p_face
1211 #define glyph face->glyph
1213 while( *psz_unicode )
1215 i_char = *psz_unicode++;
1216 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1221 if( i_char == '\n' )
1223 psz_line_start = psz_unicode;
1224 if( !(p_next = NewLine( strlen( psz_string ))) )
1226 p_line->p_next = p_next;
1227 p_line->i_width = line.xMax;
1228 p_line->i_height = face->size->metrics.height >> 6;
1229 p_line->pp_glyphs[ i ] = NULL;
1230 p_line->i_alpha = i_font_alpha;
1231 p_line->i_red = i_red;
1232 p_line->i_green = i_green;
1233 p_line->i_blue = i_blue;
1236 result.x = __MAX( result.x, line.xMax );
1237 result.y += face->size->metrics.height >> 6;
1240 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1241 i_pen_y += face->size->metrics.height >> 6;
1243 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1248 i_glyph_index = FT_Get_Char_Index( face, i_char );
1249 if( p_sys->i_use_kerning && i_glyph_index
1253 FT_Get_Kerning( face, i_previous, i_glyph_index,
1254 ft_kerning_default, &delta );
1255 i_pen_x += delta.x >> 6;
1258 p_line->p_glyph_pos[ i ].x = i_pen_x;
1259 p_line->p_glyph_pos[ i ].y = i_pen_y;
1260 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1263 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1267 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1270 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1274 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1275 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1278 FT_Done_Glyph( tmp_glyph );
1281 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1284 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1285 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1286 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1288 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1289 p_line->pp_glyphs[ i ] = NULL;
1291 p_line = NewLine( strlen( psz_string ));
1292 if( p_prev ) p_prev->p_next = p_line;
1293 else p_lines = p_line;
1295 uint32_t *psz_unicode_saved = psz_unicode;
1296 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1300 if( psz_unicode == psz_line_start )
1301 { /* try harder to break that line */
1302 psz_unicode = psz_unicode_saved;
1303 while( psz_unicode > psz_line_start &&
1304 *psz_unicode != '_' && *psz_unicode != '/' &&
1305 *psz_unicode != '\\' && *psz_unicode != '.' )
1310 if( psz_unicode == psz_line_start )
1312 msg_Warn( p_filter, "unbreakable string" );
1317 *psz_unicode = '\n';
1319 psz_unicode = psz_line_start;
1322 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1325 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1326 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1328 i_previous = i_glyph_index;
1329 i_pen_x += glyph->advance.x >> 6;
1333 p_line->i_width = line.xMax;
1334 p_line->i_height = face->size->metrics.height >> 6;
1335 p_line->pp_glyphs[ i ] = NULL;
1336 p_line->i_alpha = i_font_alpha;
1337 p_line->i_red = i_red;
1338 p_line->i_green = i_green;
1339 p_line->i_blue = i_blue;
1340 result.x = __MAX( result.x, line.xMax );
1341 result.y += line.yMax - line.yMin;
1346 p_region_out->i_x = p_region_in->i_x;
1347 p_region_out->i_y = p_region_in->i_y;
1349 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1350 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1352 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1354 free( psz_unicode_orig );
1355 FreeLines( p_lines );
1359 free( psz_unicode_orig );
1360 FreeLines( p_lines );
1361 return VLC_EGENERIC;
1364 #ifdef HAVE_FONTCONFIG
1365 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1366 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1367 bool b_italic, bool b_uline )
1369 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1373 p_style->i_font_size = i_font_size;
1374 p_style->i_font_color = i_font_color;
1375 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1376 p_style->b_italic = b_italic;
1377 p_style->b_bold = b_bold;
1378 p_style->b_underline = b_uline;
1380 p_style->psz_fontname = strdup( psz_fontname );
1385 static void DeleteStyle( ft_style_t *p_style )
1389 free( p_style->psz_fontname );
1394 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1401 if(( s1->i_font_size == s2->i_font_size ) &&
1402 ( s1->i_font_color == s2->i_font_color ) &&
1403 ( s1->b_italic == s2->b_italic ) &&
1404 ( s1->b_bold == s2->b_bold ) &&
1405 ( s1->b_underline == s2->b_underline ) &&
1406 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1413 static void IconvText( filter_t *p_filter, const char *psz_string,
1414 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1416 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1418 /* If memory hasn't been allocated for our output string, allocate it here
1419 * - the calling function must now be responsible for freeing it.
1421 if( !*ppsz_unicode )
1422 *ppsz_unicode = (uint32_t *)
1423 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1425 /* We don't need to handle a NULL pointer in *ppsz_unicode
1426 * if we are instead testing for a non NULL value like we are here */
1430 #if defined(WORDS_BIGENDIAN)
1431 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1433 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1435 if( iconv_handle != (vlc_iconv_t)-1 )
1437 char *p_in_buffer, *p_out_buffer;
1438 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1439 i_in_bytes = strlen( psz_string );
1440 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1441 i_out_bytes_left = i_out_bytes;
1442 p_in_buffer = (char *) psz_string;
1443 p_out_buffer = (char *) *ppsz_unicode;
1444 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1445 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1447 vlc_iconv_close( iconv_handle );
1451 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1452 "bytes left %u", (unsigned)i_in_bytes );
1456 *(uint32_t*)p_out_buffer = 0;
1458 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1463 msg_Warn( p_filter, "unable to do conversion" );
1468 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1469 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1472 ft_style_t *p_style = NULL;
1474 char *psz_fontname = NULL;
1475 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1476 uint32_t i_karaoke_bg_color = i_font_color;
1477 int i_font_size = p_sys->i_font_size;
1479 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1480 &i_font_color, &i_karaoke_bg_color ))
1482 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1483 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1488 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1489 bool b_uline, int i_karaoke_bgcolor,
1490 line_desc_t *p_line, uint32_t *psz_unicode,
1491 int *pi_pen_x, int i_pen_y, int *pi_start,
1492 FT_Vector *p_result )
1497 bool b_first_on_line = true;
1500 int i_pen_x_start = *pi_pen_x;
1502 uint32_t *psz_unicode_start = psz_unicode;
1504 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1506 /* Account for part of line already in position */
1507 for( i=0; i<*pi_start; i++ )
1511 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1512 ft_glyph_bbox_pixels, &glyph_size );
1514 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1515 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1516 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1517 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1523 b_first_on_line = false;
1525 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1531 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1532 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1536 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1537 ft_kerning_default, &delta );
1538 *pi_pen_x += delta.x >> 6;
1540 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1541 p_line->p_glyph_pos[ i ].y = i_pen_y;
1543 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1547 "unable to render text FT_Load_Glyph returned %d", i_error );
1548 p_line->pp_glyphs[ i ] = NULL;
1549 return VLC_EGENERIC;
1551 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1555 "unable to render text FT_Get_Glyph returned %d", i_error );
1556 p_line->pp_glyphs[ i ] = NULL;
1557 return VLC_EGENERIC;
1559 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1560 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1563 FT_Done_Glyph( tmp_glyph );
1568 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1569 p_face->size->metrics.y_scale));
1570 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1571 p_face->size->metrics.y_scale));
1573 p_line->pi_underline_offset[ i ] =
1574 ( aOffset < 0 ) ? -aOffset : aOffset;
1575 p_line->pi_underline_thickness[ i ] =
1576 ( aSize < 0 ) ? -aSize : aSize;
1578 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1579 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1580 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1581 p_line->p_fg_bg_ratio[ i ] = 0x00;
1583 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1584 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1585 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1587 for( ; i >= *pi_start; i-- )
1588 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1591 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1595 if( psz_unicode == psz_unicode_start )
1597 if( b_first_on_line )
1599 msg_Warn( p_filter, "unbreakable string" );
1600 p_line->pp_glyphs[ i ] = NULL;
1601 return VLC_EGENERIC;
1603 *pi_pen_x = i_pen_x_start;
1605 p_line->i_width = line.xMax;
1606 p_line->i_height = __MAX( p_line->i_height,
1607 p_face->size->metrics.height >> 6 );
1608 p_line->pp_glyphs[ i ] = NULL;
1610 p_result->x = __MAX( p_result->x, line.xMax );
1611 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1612 i_yMax - i_yMin ) );
1617 *psz_unicode = '\n';
1619 psz_unicode = psz_unicode_start;
1620 *pi_pen_x = i_pen_x_start;
1628 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1629 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1631 i_previous = i_glyph_index;
1632 *pi_pen_x += p_face->glyph->advance.x >> 6;
1635 p_line->i_width = line.xMax;
1636 p_line->i_height = __MAX( p_line->i_height,
1637 p_face->size->metrics.height >> 6 );
1638 p_line->pp_glyphs[ i ] = NULL;
1640 p_result->x = __MAX( p_result->x, line.xMax );
1641 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1642 line.yMax - line.yMin ) );
1646 /* Get rid of any text processed - if necessary repositioning
1647 * at the start of a new line of text
1651 *psz_unicode_start = '\0';
1653 else if( psz_unicode > psz_unicode_start )
1655 for( i=0; psz_unicode[ i ]; i++ )
1656 psz_unicode_start[ i ] = psz_unicode[ i ];
1657 psz_unicode_start[ i ] = '\0';
1663 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1664 uint32_t **psz_text_out, uint32_t *pi_runs,
1665 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1666 ft_style_t *p_style )
1668 uint32_t i_string_length = 0;
1670 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1671 *psz_text_out += i_string_length;
1673 if( ppp_styles && ppi_run_lengths )
1677 /* XXX this logic looks somewhat broken */
1681 *ppp_styles = realloc_or_free( *ppp_styles,
1682 *pi_runs * sizeof( ft_style_t * ) );
1684 else if( *pi_runs == 1 )
1686 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1689 /* We have just malloc'ed this memory successfully -
1690 * *pi_runs HAS to be within the memory area of *ppp_styles */
1693 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1697 /* XXX more iffy logic */
1699 if( *ppi_run_lengths )
1701 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1702 *pi_runs * sizeof( uint32_t ) );
1704 else if( *pi_runs == 1 )
1706 *ppi_run_lengths = (uint32_t *)
1707 malloc( *pi_runs * sizeof( uint32_t ) );
1710 /* same remarks here */
1711 if( *ppi_run_lengths )
1713 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1716 /* If we couldn't use the p_style argument due to memory allocation
1717 * problems above, release it here.
1719 if( p_style ) DeleteStyle( p_style );
1722 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1726 for( k=0; k < p_sys->i_font_attachments; k++ )
1728 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1730 FT_Face p_face = NULL;
1732 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1740 bool match = !strcasecmp( p_face->family_name,
1741 p_style->psz_fontname );
1743 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1744 match = match && p_style->b_bold;
1746 match = match && !p_style->b_bold;
1748 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1749 match = match && p_style->b_italic;
1751 match = match && !p_style->b_italic;
1759 FT_Done_Face( p_face );
1764 return VLC_EGENERIC;
1767 static int ProcessLines( filter_t *p_filter,
1772 uint32_t *pi_run_lengths,
1773 ft_style_t **pp_styles,
1774 line_desc_t **pp_lines,
1776 FT_Vector *p_result,
1780 uint32_t *pi_k_run_lengths,
1781 uint32_t *pi_k_durations )
1783 filter_sys_t *p_sys = p_filter->p_sys;
1784 ft_style_t **pp_char_styles;
1785 int *p_new_positions = NULL;
1786 int8_t *p_levels = NULL;
1787 uint8_t *pi_karaoke_bar = NULL;
1791 /* Assign each character in the text string its style explicitly, so that
1792 * after the characters have been shuffled around by Fribidi, we can re-apply
1793 * the styles, and to simplify the calculation of runs within a line.
1795 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1796 if( !pp_char_styles )
1801 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1802 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1803 * we just won't be able to display the progress bar; at least we'll
1809 for( j = 0; j < i_runs; j++ )
1810 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1811 pp_char_styles[ i++ ] = pp_styles[ j ];
1813 #if defined(HAVE_FRIBIDI)
1815 ft_style_t **pp_char_styles_new;
1816 int *p_old_positions;
1817 uint32_t *p_fribidi_string;
1818 int start_pos, pos = 0;
1820 pp_char_styles_new = (ft_style_t **)
1821 malloc( i_len * sizeof( ft_style_t * ));
1823 p_fribidi_string = (uint32_t *)
1824 malloc( (i_len + 1) * sizeof(uint32_t) );
1825 p_old_positions = (int *)
1826 malloc( (i_len + 1) * sizeof( int ) );
1827 p_new_positions = (int *)
1828 malloc( (i_len + 1) * sizeof( int ) );
1829 p_levels = (int8_t *)
1830 malloc( (i_len + 1) * sizeof( int8_t ) );
1832 if( ! pp_char_styles_new ||
1833 ! p_fribidi_string ||
1834 ! p_old_positions ||
1835 ! p_new_positions ||
1839 free( p_old_positions );
1840 free( p_new_positions );
1841 free( p_fribidi_string );
1842 free( pp_char_styles_new );
1843 free( pi_karaoke_bar );
1845 free( pp_char_styles );
1849 /* Do bidi conversion line-by-line */
1852 while(pos < i_len) {
1853 if (psz_text[pos] != '\n')
1855 p_fribidi_string[pos] = psz_text[pos];
1856 pp_char_styles_new[pos] = pp_char_styles[pos];
1857 p_new_positions[pos] = pos;
1862 while(pos < i_len) {
1863 if (psz_text[pos] == '\n')
1867 if (pos > start_pos)
1869 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1870 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1871 pos - start_pos, &base_dir,
1872 (FriBidiChar*)p_fribidi_string + start_pos,
1873 p_new_positions + start_pos,
1875 p_levels + start_pos );
1876 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1878 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1879 p_old_positions[ j - start_pos ] ];
1880 p_new_positions[ j ] += start_pos;
1884 free( p_old_positions );
1885 free( pp_char_styles );
1886 pp_char_styles = pp_char_styles_new;
1887 psz_text = p_fribidi_string;
1888 p_fribidi_string[ i_len ] = 0;
1891 /* Work out the karaoke */
1892 if( pi_karaoke_bar )
1894 int64_t i_last_duration = 0;
1895 int64_t i_duration = 0;
1896 int64_t i_start_pos = 0;
1897 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1899 for( k = 0; k< i_k_runs; k++ )
1901 double fraction = 0.0;
1903 i_duration += pi_k_durations[ k ];
1905 if( i_duration < i_elapsed )
1907 /* Completely finished this run-length -
1908 * let it render normally */
1912 else if( i_elapsed < i_last_duration )
1914 /* Haven't got up to this segment yet -
1915 * render it completely in karaoke BG mode */
1921 /* Partway through this run */
1923 fraction = (double)(i_elapsed - i_last_duration) /
1924 (double)pi_k_durations[ k ];
1926 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1928 double shade = pi_k_run_lengths[ k ] * fraction;
1930 if( p_new_positions )
1931 j = p_new_positions[ i_start_pos + i ];
1933 j = i_start_pos + i;
1935 if( i < (uint32_t)shade )
1936 pi_karaoke_bar[ j ] = 0xff;
1937 else if( (double)i > shade )
1938 pi_karaoke_bar[ j ] = 0x00;
1941 shade -= (int)shade;
1942 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1943 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1947 i_last_duration = i_duration;
1948 i_start_pos += pi_k_run_lengths[ k ];
1952 free( p_new_positions );
1954 FT_Vector tmp_result;
1956 line_desc_t *p_line = NULL;
1957 line_desc_t *p_prev = NULL;
1963 p_result->x = p_result->y = 0;
1964 tmp_result.x = tmp_result.y = 0;
1967 for( k = 0; k <= (uint32_t) i_len; k++ )
1969 if( ( k == (uint32_t) i_len ) ||
1971 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1973 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1975 /* End of the current style run */
1976 FT_Face p_face = NULL;
1979 /* Look for a match amongst our attachments first */
1980 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1984 char *psz_fontfile = NULL;
1986 psz_fontfile = FontConfig_Select( NULL,
1987 p_style->psz_fontname,
1991 if( psz_fontfile && ! *psz_fontfile )
1993 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1994 " so using default font", p_style->psz_fontname,
1995 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1996 (p_style->b_bold ? "(Bold)" :
1997 (p_style->b_italic ? "(Italic)" : ""))) );
1998 free( psz_fontfile );
1999 psz_fontfile = NULL;
2004 if( FT_New_Face( p_sys->p_library,
2005 psz_fontfile, i_idx, &p_face ) )
2007 free( psz_fontfile );
2008 free( pp_char_styles );
2009 #if defined(HAVE_FRIBIDI)
2012 free( pi_karaoke_bar );
2013 return VLC_EGENERIC;
2015 free( psz_fontfile );
2019 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2021 /* We've loaded a font face which is unhelpful for actually
2022 * rendering text - fallback to the default one.
2024 FT_Done_Face( p_face );
2028 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2029 ft_encoding_unicode ) ||
2030 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2031 p_style->i_font_size ) )
2033 if( p_face ) FT_Done_Face( p_face );
2034 free( pp_char_styles );
2035 #if defined(HAVE_FRIBIDI)
2038 free( pi_karaoke_bar );
2039 return VLC_EGENERIC;
2041 p_sys->i_use_kerning =
2042 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2045 uint32_t *psz_unicode = (uint32_t *)
2046 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2049 if( p_face ) FT_Done_Face( p_face );
2050 free( pp_char_styles );
2051 free( psz_unicode );
2052 #if defined(HAVE_FRIBIDI)
2055 free( pi_karaoke_bar );
2058 memcpy( psz_unicode, psz_text + i_prev,
2059 sizeof( uint32_t ) * ( k - i_prev ) );
2060 psz_unicode[ k - i_prev ] = 0;
2061 while( *psz_unicode )
2065 if( !(p_line = NewLine( i_len - i_prev)) )
2067 if( p_face ) FT_Done_Face( p_face );
2068 free( pp_char_styles );
2069 free( psz_unicode );
2070 #if defined(HAVE_FRIBIDI)
2073 free( pi_karaoke_bar );
2076 /* New Color mode only works in YUVA rendering mode --
2077 * (RGB mode has palette constraints on it). We therefore
2078 * need to populate the legacy colour fields also.
2080 p_line->b_new_color_mode = true;
2081 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2082 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2083 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2084 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2085 p_line->p_next = NULL;
2087 i_pen_y += tmp_result.y;
2091 if( p_prev ) p_prev->p_next = p_line;
2092 else *pp_lines = p_line;
2095 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2096 p_style->i_font_color, p_style->b_underline,
2097 p_style->i_karaoke_bg_color,
2098 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2099 &tmp_result ) != VLC_SUCCESS )
2101 if( p_face ) FT_Done_Face( p_face );
2102 free( pp_char_styles );
2103 free( psz_unicode );
2104 #if defined(HAVE_FRIBIDI)
2107 free( pi_karaoke_bar );
2108 return VLC_EGENERIC;
2113 p_result->x = __MAX( p_result->x, tmp_result.x );
2114 p_result->y += tmp_result.y;
2119 if( *psz_unicode == '\n')
2123 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2125 *c_ptr = *(c_ptr+1);
2130 free( psz_unicode );
2131 if( p_face ) FT_Done_Face( p_face );
2135 free( pp_char_styles );
2136 #if defined(HAVE_FRIBIDI)
2141 p_result->x = __MAX( p_result->x, tmp_result.x );
2142 p_result->y += tmp_result.y;
2145 if( pi_karaoke_bar )
2148 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2150 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2152 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2156 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2158 /* 100% BG colour will render faster if we
2159 * instead make it 100% FG colour, so leave
2160 * the ratio alone and copy the value across
2162 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2166 if( pi_karaoke_bar[ i ] & 0x80 )
2168 /* Swap Left and Right sides over for Right aligned
2169 * language text (eg. Arabic, Hebrew)
2171 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2173 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2174 p_line->p_bg_rgb[ k ] = i_tmp;
2176 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2179 /* Jump over the '\n' at the line-end */
2182 free( pi_karaoke_bar );
2188 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2189 subpicture_region_t *p_region_in )
2191 int rv = VLC_SUCCESS;
2192 stream_t *p_sub = NULL;
2193 xml_reader_t *p_xml_reader = NULL;
2195 if( !p_region_in || !p_region_in->psz_html )
2196 return VLC_EGENERIC;
2198 /* Reset the default fontsize in case screen metrics have changed */
2199 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2201 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2202 (uint8_t *) p_region_in->psz_html,
2203 strlen( p_region_in->psz_html ),
2207 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2208 if( p_filter->p_sys->p_xml )
2210 bool b_karaoke = false;
2212 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2215 /* Look for Root Node */
2216 if( xml_ReaderRead( p_xml_reader ) == 1 )
2218 char *psz_node = xml_ReaderName( p_xml_reader );
2220 if( !strcasecmp( "karaoke", psz_node ) )
2222 /* We're going to have to render the text a number
2223 * of times to show the progress marker on the text.
2225 var_SetBool( p_filter, "text-rerender", true );
2228 else if( !strcasecmp( "text", psz_node ) )
2234 /* Only text and karaoke tags are supported */
2235 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2236 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2237 p_xml_reader = NULL;
2249 uint32_t i_runs = 0;
2250 uint32_t i_k_runs = 0;
2251 uint32_t *pi_run_lengths = NULL;
2252 uint32_t *pi_k_run_lengths = NULL;
2253 uint32_t *pi_k_durations = NULL;
2254 ft_style_t **pp_styles = NULL;
2256 line_desc_t *p_lines = NULL;
2258 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2259 sizeof( uint32_t ) );
2264 rv = ProcessNodes( p_filter, p_xml_reader,
2265 p_region_in->p_style, psz_text, &i_len,
2266 &i_runs, &pi_run_lengths, &pp_styles,
2268 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2271 p_region_out->i_x = p_region_in->i_x;
2272 p_region_out->i_y = p_region_in->i_y;
2274 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2276 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2277 pi_run_lengths, pp_styles, &p_lines, &result,
2278 b_karaoke, i_k_runs, pi_k_run_lengths,
2282 for( k=0; k<i_runs; k++)
2283 DeleteStyle( pp_styles[k] );
2285 free( pi_run_lengths );
2288 /* Don't attempt to render text that couldn't be layed out
2291 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2293 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2295 Render( p_filter, p_region_out, p_lines,
2296 result.x, result.y );
2300 RenderYUVA( p_filter, p_region_out, p_lines,
2301 result.x, result.y );
2305 FreeLines( p_lines );
2307 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2310 stream_Delete( p_sub );
2316 static char* FontConfig_Select( FcConfig* priv, const char* family,
2317 bool b_bold, bool b_italic, int *i_idx )
2320 FcPattern *pat, *p_pat;
2324 pat = FcPatternCreate();
2325 if (!pat) return NULL;
2327 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2328 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2329 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2330 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2332 FcDefaultSubstitute( pat );
2334 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2336 FcPatternDestroy( pat );
2340 p_pat = FcFontMatch( priv, pat, &result );
2341 FcPatternDestroy( pat );
2342 if( !p_pat ) return NULL;
2344 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2345 || ( val_b != FcTrue ) )
2347 FcPatternDestroy( p_pat );
2350 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2355 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2357 FcPatternDestroy( p_pat );
2362 if( strcasecmp((const char*)val_s, family ) != 0 )
2363 msg_Warn( p_filter, "fontconfig: selected font family is not"
2364 "the requested one: '%s' != '%s'\n",
2365 (const char*)val_s, family );
2368 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2370 FcPatternDestroy( p_pat );
2374 FcPatternDestroy( p_pat );
2375 return strdup( (const char*)val_s );
2379 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2380 uint32_t **psz_text_out, uint32_t *pi_runs,
2381 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2382 ft_style_t *p_style )
2384 VLC_UNUSED(p_filter);
2385 VLC_UNUSED(psz_text_in);
2386 VLC_UNUSED(psz_text_out);
2387 VLC_UNUSED(pi_runs);
2388 VLC_UNUSED(ppi_run_lengths);
2389 VLC_UNUSED(ppp_styles);
2390 VLC_UNUSED(p_style);
2393 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2394 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2398 VLC_UNUSED(p_fonts);
2400 VLC_UNUSED(b_italic);
2401 VLC_UNUSED(b_uline);
2406 static void FreeLine( line_desc_t *p_line )
2409 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2411 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2413 free( p_line->pp_glyphs );
2414 free( p_line->p_glyph_pos );
2415 free( p_line->p_fg_rgb );
2416 free( p_line->p_bg_rgb );
2417 free( p_line->p_fg_bg_ratio );
2418 free( p_line->pi_underline_offset );
2419 free( p_line->pi_underline_thickness );
2423 static void FreeLines( line_desc_t *p_lines )
2425 line_desc_t *p_line, *p_next;
2427 if( !p_lines ) return;
2429 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2431 p_next = p_line->p_next;
2436 static line_desc_t *NewLine( int i_count )
2438 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2440 if( !p_line ) return NULL;
2441 p_line->i_height = 0;
2442 p_line->i_width = 0;
2443 p_line->p_next = NULL;
2445 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2446 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2447 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2448 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2449 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2450 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2451 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2452 if( ( p_line->pp_glyphs == NULL ) ||
2453 ( p_line->p_glyph_pos == NULL ) ||
2454 ( p_line->p_fg_rgb == NULL ) ||
2455 ( p_line->p_bg_rgb == NULL ) ||
2456 ( p_line->p_fg_bg_ratio == NULL ) ||
2457 ( p_line->pi_underline_offset == NULL ) ||
2458 ( p_line->pi_underline_thickness == NULL ) )
2460 free( p_line->pi_underline_thickness );
2461 free( p_line->pi_underline_offset );
2462 free( p_line->p_fg_rgb );
2463 free( p_line->p_bg_rgb );
2464 free( p_line->p_fg_bg_ratio );
2465 free( p_line->p_glyph_pos );
2466 free( p_line->pp_glyphs );
2470 p_line->pp_glyphs[0] = NULL;
2471 p_line->b_new_color_mode = false;
2476 static int GetFontSize( filter_t *p_filter )
2478 filter_sys_t *p_sys = p_filter->p_sys;
2482 if( p_sys->i_default_font_size )
2484 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2485 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2487 i_size = p_sys->i_default_font_size;
2491 var_Get( p_filter, "freetype-rel-fontsize", &val );
2494 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2495 p_filter->p_sys->i_display_height =
2496 p_filter->fmt_out.video.i_height;
2501 msg_Warn( p_filter, "invalid fontsize, using 12" );
2502 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2503 i_size = 12 * val.i_int / 1000;
2510 static int SetFontSize( filter_t *p_filter, int i_size )
2512 filter_sys_t *p_sys = p_filter->p_sys;
2516 i_size = GetFontSize( p_filter );
2518 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2521 p_sys->i_font_size = i_size;
2523 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2525 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2526 return VLC_EGENERIC;
2532 static void YUVFromRGB( uint32_t i_argb,
2533 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2535 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2536 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2537 int i_blue = ( i_argb & 0x000000ff );
2539 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2540 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2541 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2542 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2543 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2544 -585 * i_blue + 4096 + 1048576) >> 13, 240);