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>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
57 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
58 #define FC_DEFAULT_FONT "Arial Black"
59 #elif defined( SYS_BEOS )
60 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
61 #define FC_DEFAULT_FONT "Swiss"
62 #elif defined( WIN32 )
63 #define DEFAULT_FONT "" /* Default font found at run-time */
64 #define FC_DEFAULT_FONT "Arial"
65 #elif defined( HAVE_MAEMO )
66 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
67 #define FC_DEFAULT_FONT "Nokia Sans Bold"
69 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
70 #define FC_DEFAULT_FONT "Serif Bold"
73 #if defined(HAVE_FRIBIDI)
74 #include <fribidi/fribidi.h>
77 #ifdef HAVE_FONTCONFIG
78 #include <fontconfig/fontconfig.h>
80 #define DEFAULT_FONT FC_DEFAULT_FONT
85 /*****************************************************************************
87 *****************************************************************************/
88 static int Create ( vlc_object_t * );
89 static void Destroy( vlc_object_t * );
91 #define FONT_TEXT N_("Font")
93 #ifdef HAVE_FONTCONFIG
94 #define FONT_LONGTEXT N_("Font family for the font you want to use")
96 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
99 #define FONTSIZE_TEXT N_("Font size in pixels")
100 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
101 "that will be rendered on the video. " \
102 "If set to something different than 0 this option will override the " \
103 "relative font size." )
104 #define OPACITY_TEXT N_("Opacity")
105 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
106 "text that will be rendered on the video. 0 = transparent, " \
107 "255 = totally opaque. " )
108 #define COLOR_TEXT N_("Text default color")
109 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
110 "the video. This must be an hexadecimal (like HTML colors). The first two "\
111 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
112 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
113 #define FONTSIZER_TEXT N_("Relative font size")
114 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
115 "fonts that will be rendered on the video. If absolute font size is set, "\
116 "relative size will be overriden." )
118 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
119 static const char *const ppsz_sizes_text[] = {
120 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
121 #define YUVP_TEXT N_("Use YUVP renderer")
122 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
123 "This option is only needed if you want to encode into DVB subtitles" )
124 #define EFFECT_TEXT N_("Font Effect")
125 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
126 "text to improve its readability." )
128 #define EFFECT_BACKGROUND 1
129 #define EFFECT_OUTLINE 2
130 #define EFFECT_OUTLINE_FAT 3
132 static int const pi_effects[] = { 1, 2, 3 };
133 static const char *const ppsz_effects_text[] = {
134 N_("Background"),N_("Outline"), N_("Fat Outline") };
135 static const int pi_color_values[] = {
136 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
137 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
138 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
140 static const char *const ppsz_color_descriptions[] = {
141 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
142 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
143 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
146 set_shortname( N_("Text renderer"))
147 set_description( N_("Freetype2 font renderer") )
148 set_category( CAT_VIDEO )
149 set_subcategory( SUBCAT_VIDEO_SUBPIC )
151 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
154 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
155 FONTSIZE_LONGTEXT, true )
157 /* opacity valid on 0..255, with default 255 = fully opaque */
158 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
159 OPACITY_TEXT, OPACITY_LONGTEXT, true )
161 /* hook to the color values list, with default 0x00ffffff = white */
162 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
163 COLOR_LONGTEXT, false )
164 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
166 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
167 FONTSIZER_LONGTEXT, false )
168 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
169 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
170 EFFECT_LONGTEXT, false )
171 change_integer_list( pi_effects, ppsz_effects_text, NULL )
173 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
174 YUVP_LONGTEXT, true )
175 set_capability( "text renderer", 100 )
176 add_shortcut( "text" )
177 set_callbacks( Create, Destroy )
182 /*****************************************************************************
184 *****************************************************************************/
186 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
187 static int RenderText( filter_t *, subpicture_region_t *,
188 subpicture_region_t * );
189 #ifdef HAVE_FONTCONFIG
190 static int RenderHtml( filter_t *, subpicture_region_t *,
191 subpicture_region_t * );
192 static char *FontConfig_Select( FcConfig *, const char *,
197 static int LoadFontsFromAttachments( filter_t *p_filter );
199 static int GetFontSize( filter_t *p_filter );
200 static int SetFontSize( filter_t *, int );
201 static void YUVFromRGB( uint32_t i_argb,
202 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
204 typedef struct line_desc_t line_desc_t;
207 /** NULL-terminated list of glyphs making the string */
208 FT_BitmapGlyph *pp_glyphs;
209 /** list of relative positions for the glyphs */
210 FT_Vector *p_glyph_pos;
211 /** list of RGB information for styled text
212 * -- if the rendering mode supports it (RenderYUVA) and
213 * b_new_color_mode is set, then it becomes possible to
214 * have multicoloured text within the subtitles. */
217 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
218 bool b_new_color_mode;
219 /** underline information -- only supplied if text should be underlined */
220 int *pi_underline_offset;
221 uint16_t *pi_underline_thickness;
225 int i_red, i_green, i_blue;
230 static line_desc_t *NewLine( int );
235 uint32_t i_font_color; /* ARGB */
236 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 /* Strikethrough doesn't get broken */
783 for( z = x - i_line_thickness;
784 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
787 if( p_next_glyph && ( z >= i_extra ) )
789 int i_row = i_line_offset + p_next_glyph->top + y;
791 if( ( p_next_glyph->bitmap.rows > i_row ) &&
792 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
797 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
799 int i_row = i_line_offset + p_this_glyph->top + y;
801 if( ( p_this_glyph->bitmap.rows > i_row ) &&
802 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
811 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
812 p_dst_u[i_offset+x] = i_u;
813 p_dst_v[i_offset+x] = i_v;
814 p_dst_a[i_offset+x] = 255;
821 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
823 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
824 int i_pitch = p_region->p_picture->A_PITCH;
827 for( ; p_line != NULL; p_line = p_line->p_next )
829 int i_glyph_tmax=0, i = 0;
830 int i_bitmap_offset, i_offset, i_align_offset = 0;
831 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
833 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
834 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
837 if( p_line->i_width < i_width )
839 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
841 i_align_offset = i_width - p_line->i_width;
843 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
845 i_align_offset = ( i_width - p_line->i_width ) / 2;
849 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
851 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
853 i_offset = ( p_line->p_glyph_pos[ i ].y +
854 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
855 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
856 i_align_offset +xoffset;
858 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
860 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
862 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
863 if( p_dst[i_offset+x] <
864 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
866 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
875 /*****************************************************************************
876 * Render: place string in picture
877 *****************************************************************************
878 * This function merges the previously rendered freetype glyphs into a picture
879 *****************************************************************************/
880 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
881 line_desc_t *p_line, int i_width, int i_height )
883 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
885 int i, x, y, i_pitch, i_alpha;
886 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
888 if( i_width == 0 || i_height == 0 )
891 /* Create a new subpicture region */
892 memset( &fmt, 0, sizeof(video_format_t) );
893 fmt.i_chroma = VLC_CODEC_YUVA;
894 fmt.i_width = fmt.i_visible_width = i_width + 6;
895 fmt.i_height = fmt.i_visible_height = i_height + 6;
896 if( p_region->fmt.i_visible_width > 0 )
897 fmt.i_visible_width = p_region->fmt.i_visible_width;
898 if( p_region->fmt.i_visible_height > 0 )
899 fmt.i_visible_height = p_region->fmt.i_visible_height;
900 fmt.i_x_offset = fmt.i_y_offset = 0;
902 p_region->p_picture = picture_NewFromFormat( &fmt );
903 if( !p_region->p_picture )
907 /* Calculate text color components */
908 YUVFromRGB( (p_line->i_red << 16) |
909 (p_line->i_green << 8) |
912 i_alpha = p_line->i_alpha;
914 p_dst_y = p_region->p_picture->Y_PIXELS;
915 p_dst_u = p_region->p_picture->U_PIXELS;
916 p_dst_v = p_region->p_picture->V_PIXELS;
917 p_dst_a = p_region->p_picture->A_PIXELS;
918 i_pitch = p_region->p_picture->A_PITCH;
920 /* Initialize the region pixels */
921 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
923 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
924 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
925 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
926 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
930 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
931 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
932 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
933 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
935 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
936 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
938 DrawBlack( p_line, i_width, p_region, 0, 0);
939 DrawBlack( p_line, i_width, p_region, -1, 0);
940 DrawBlack( p_line, i_width, p_region, 0, -1);
941 DrawBlack( p_line, i_width, p_region, 1, 0);
942 DrawBlack( p_line, i_width, p_region, 0, 1);
945 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
950 DrawBlack( p_line, i_width, p_region, 1, 1);
952 DrawBlack( p_line, i_width, p_region, -2, 0);
953 DrawBlack( p_line, i_width, p_region, 0, -2);
954 DrawBlack( p_line, i_width, p_region, 2, 0);
955 DrawBlack( p_line, i_width, p_region, 0, 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);
960 DrawBlack( p_line, i_width, p_region, 2, 2);
962 DrawBlack( p_line, i_width, p_region, -3, 0);
963 DrawBlack( p_line, i_width, p_region, 0, -3);
964 DrawBlack( p_line, i_width, p_region, 3, 0);
965 DrawBlack( p_line, i_width, p_region, 0, 3);
968 for( ; p_line != NULL; p_line = p_line->p_next )
970 int i_glyph_tmax = 0;
971 int i_bitmap_offset, i_offset, i_align_offset = 0;
972 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
974 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
975 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
978 if( p_line->i_width < i_width )
980 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
982 i_align_offset = i_width - p_line->i_width;
984 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
986 i_align_offset = ( i_width - p_line->i_width ) / 2;
990 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
992 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
994 i_offset = ( p_line->p_glyph_pos[ i ].y +
995 i_glyph_tmax - p_glyph->top + 3 ) *
996 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
999 if( p_line->b_new_color_mode )
1001 /* Every glyph can (and in fact must) have its own color */
1002 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1005 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1007 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1009 uint8_t i_y_local = i_y;
1010 uint8_t i_u_local = i_u;
1011 uint8_t i_v_local = i_v;
1013 if( p_line->p_fg_bg_ratio != 0x00 )
1015 int i_split = p_glyph->bitmap.width *
1016 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1020 YUVFromRGB( p_line->p_bg_rgb[ i ],
1021 &i_y_local, &i_u_local, &i_v_local );
1025 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1027 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1028 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1030 p_dst_u[i_offset+x] = i_u;
1031 p_dst_v[i_offset+x] = i_v;
1033 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1034 p_dst_a[i_offset+x] = 0xff;
1037 i_offset += i_pitch;
1040 if( p_line->pi_underline_thickness[ i ] )
1042 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1043 p_line->pi_underline_offset[ i ],
1044 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1045 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1046 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1047 i_glyph_tmax, i_align_offset,
1054 /* Apply the alpha setting */
1055 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1056 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1062 * This function renders a text subpicture region into another one.
1063 * It also calculates the size needed for this string, and renders the
1064 * needed glyphs into memory. It is used as pf_add_string callback in
1065 * the vout method by this module
1067 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1068 subpicture_region_t *p_region_in )
1070 filter_sys_t *p_sys = p_filter->p_sys;
1071 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1072 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1073 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1074 int i_string_length;
1076 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1077 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1087 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1088 psz_string = p_region_in->psz_text;
1089 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1091 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1092 i_scale = val.i_int;
1094 if( p_region_in->p_style )
1096 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1097 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1098 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1102 i_font_color = p_sys->i_font_color;
1103 i_font_alpha = 255 - p_sys->i_font_opacity;
1104 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1107 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1108 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1109 SetFontSize( p_filter, i_font_size );
1111 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1112 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1113 i_blue = i_font_color & 0x000000FF;
1115 result.x = result.y = 0;
1116 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1118 psz_unicode = psz_unicode_orig =
1119 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1120 if( psz_unicode == NULL )
1122 #if defined(WORDS_BIGENDIAN)
1123 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1125 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1127 if( iconv_handle == (vlc_iconv_t)-1 )
1129 msg_Warn( p_filter, "unable to do conversion" );
1135 const char *p_in_buffer = psz_string;
1136 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1137 i_in_bytes = strlen( psz_string );
1138 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1139 i_out_bytes_left = i_out_bytes;
1140 p_out_buffer = (char *)psz_unicode;
1141 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1143 &p_out_buffer, &i_out_bytes_left );
1145 vlc_iconv_close( iconv_handle );
1149 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1150 "bytes left %u", (unsigned)i_in_bytes );
1153 *(uint32_t*)p_out_buffer = 0;
1154 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1157 #if defined(HAVE_FRIBIDI)
1159 uint32_t *p_fribidi_string;
1160 int32_t start_pos, pos = 0;
1162 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1163 if( !p_fribidi_string )
1166 /* Do bidi conversion line-by-line */
1167 while( pos < i_string_length )
1169 while( pos < i_string_length )
1171 i_char = psz_unicode[pos];
1172 if (i_char != '\r' && i_char != '\n')
1174 p_fribidi_string[pos] = i_char;
1178 while( pos < i_string_length )
1180 i_char = psz_unicode[pos];
1181 if (i_char == '\r' || i_char == '\n')
1185 if (pos > start_pos)
1187 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1188 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1191 (FriBidiChar*)p_fribidi_string + start_pos,
1196 free( psz_unicode_orig );
1197 psz_unicode = psz_unicode_orig = p_fribidi_string;
1198 p_fribidi_string[ i_string_length ] = 0;
1202 /* Calculate relative glyph positions and a bounding box for the
1204 if( !(p_line = NewLine( strlen( psz_string ))) )
1207 i_pen_x = i_pen_y = 0;
1209 psz_line_start = psz_unicode;
1211 #define face p_sys->p_face
1212 #define glyph face->glyph
1214 while( *psz_unicode )
1216 i_char = *psz_unicode++;
1217 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1222 if( i_char == '\n' )
1224 psz_line_start = psz_unicode;
1225 if( !(p_next = NewLine( strlen( psz_string ))) )
1227 p_line->p_next = p_next;
1228 p_line->i_width = line.xMax;
1229 p_line->i_height = face->size->metrics.height >> 6;
1230 p_line->pp_glyphs[ i ] = NULL;
1231 p_line->i_alpha = i_font_alpha;
1232 p_line->i_red = i_red;
1233 p_line->i_green = i_green;
1234 p_line->i_blue = i_blue;
1237 result.x = __MAX( result.x, line.xMax );
1238 result.y += face->size->metrics.height >> 6;
1241 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1242 i_pen_y += face->size->metrics.height >> 6;
1244 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1249 i_glyph_index = FT_Get_Char_Index( face, i_char );
1250 if( p_sys->i_use_kerning && i_glyph_index
1254 FT_Get_Kerning( face, i_previous, i_glyph_index,
1255 ft_kerning_default, &delta );
1256 i_pen_x += delta.x >> 6;
1259 p_line->p_glyph_pos[ i ].x = i_pen_x;
1260 p_line->p_glyph_pos[ i ].y = i_pen_y;
1261 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1264 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1268 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1271 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1275 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1276 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1279 FT_Done_Glyph( tmp_glyph );
1282 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1285 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1286 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1287 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1289 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1290 p_line->pp_glyphs[ i ] = NULL;
1292 p_line = NewLine( strlen( psz_string ));
1293 if( p_prev ) p_prev->p_next = p_line;
1294 else p_lines = p_line;
1296 uint32_t *psz_unicode_saved = psz_unicode;
1297 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1301 if( psz_unicode == psz_line_start )
1302 { /* try harder to break that line */
1303 psz_unicode = psz_unicode_saved;
1304 while( psz_unicode > psz_line_start &&
1305 *psz_unicode != '_' && *psz_unicode != '/' &&
1306 *psz_unicode != '\\' && *psz_unicode != '.' )
1311 if( psz_unicode == psz_line_start )
1313 msg_Warn( p_filter, "unbreakable string" );
1318 *psz_unicode = '\n';
1320 psz_unicode = psz_line_start;
1323 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1326 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1327 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1329 i_previous = i_glyph_index;
1330 i_pen_x += glyph->advance.x >> 6;
1334 p_line->i_width = line.xMax;
1335 p_line->i_height = face->size->metrics.height >> 6;
1336 p_line->pp_glyphs[ i ] = NULL;
1337 p_line->i_alpha = i_font_alpha;
1338 p_line->i_red = i_red;
1339 p_line->i_green = i_green;
1340 p_line->i_blue = i_blue;
1341 result.x = __MAX( result.x, line.xMax );
1342 result.y += line.yMax - line.yMin;
1347 p_region_out->i_x = p_region_in->i_x;
1348 p_region_out->i_y = p_region_in->i_y;
1350 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1351 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1353 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1355 free( psz_unicode_orig );
1356 FreeLines( p_lines );
1360 free( psz_unicode_orig );
1361 FreeLines( p_lines );
1362 return VLC_EGENERIC;
1365 #ifdef HAVE_FONTCONFIG
1366 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1367 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1368 bool b_italic, bool b_uline, bool b_through )
1370 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1374 p_style->i_font_size = i_font_size;
1375 p_style->i_font_color = i_font_color;
1376 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1377 p_style->b_italic = b_italic;
1378 p_style->b_bold = b_bold;
1379 p_style->b_underline = b_uline;
1380 p_style->b_through = b_through;
1382 p_style->psz_fontname = strdup( psz_fontname );
1387 static void DeleteStyle( ft_style_t *p_style )
1391 free( p_style->psz_fontname );
1396 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1403 if(( s1->i_font_size == s2->i_font_size ) &&
1404 ( s1->i_font_color == s2->i_font_color ) &&
1405 ( s1->b_italic == s2->b_italic ) &&
1406 ( s1->b_through == s2->b_through ) &&
1407 ( s1->b_bold == s2->b_bold ) &&
1408 ( s1->b_underline == s2->b_underline ) &&
1409 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1416 static void IconvText( filter_t *p_filter, const char *psz_string,
1417 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1419 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1421 /* If memory hasn't been allocated for our output string, allocate it here
1422 * - the calling function must now be responsible for freeing it.
1424 if( !*ppsz_unicode )
1425 *ppsz_unicode = (uint32_t *)
1426 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1428 /* We don't need to handle a NULL pointer in *ppsz_unicode
1429 * if we are instead testing for a non NULL value like we are here */
1433 #if defined(WORDS_BIGENDIAN)
1434 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1436 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1438 if( iconv_handle != (vlc_iconv_t)-1 )
1440 char *p_in_buffer, *p_out_buffer;
1441 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1442 i_in_bytes = strlen( psz_string );
1443 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1444 i_out_bytes_left = i_out_bytes;
1445 p_in_buffer = (char *) psz_string;
1446 p_out_buffer = (char *) *ppsz_unicode;
1447 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1448 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1450 vlc_iconv_close( iconv_handle );
1454 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1455 "bytes left %u", (unsigned)i_in_bytes );
1459 *(uint32_t*)p_out_buffer = 0;
1461 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1466 msg_Warn( p_filter, "unable to do conversion" );
1471 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1472 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1473 bool b_uline, bool b_through )
1475 ft_style_t *p_style = NULL;
1477 char *psz_fontname = NULL;
1478 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1479 uint32_t i_karaoke_bg_color = i_font_color;
1480 int i_font_size = p_sys->i_font_size;
1482 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1483 &i_font_color, &i_karaoke_bg_color ))
1485 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1486 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1491 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1492 bool b_uline, bool b_through, int i_karaoke_bgcolor,
1493 line_desc_t *p_line, uint32_t *psz_unicode,
1494 int *pi_pen_x, int i_pen_y, int *pi_start,
1495 FT_Vector *p_result )
1500 bool b_first_on_line = true;
1503 int i_pen_x_start = *pi_pen_x;
1505 uint32_t *psz_unicode_start = psz_unicode;
1507 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1509 /* Account for part of line already in position */
1510 for( i=0; i<*pi_start; i++ )
1514 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1515 ft_glyph_bbox_pixels, &glyph_size );
1517 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1518 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1519 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1520 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1526 b_first_on_line = false;
1528 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1534 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1535 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1539 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1540 ft_kerning_default, &delta );
1541 *pi_pen_x += delta.x >> 6;
1543 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1544 p_line->p_glyph_pos[ i ].y = i_pen_y;
1546 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1550 "unable to render text FT_Load_Glyph returned %d", i_error );
1551 p_line->pp_glyphs[ i ] = NULL;
1552 return VLC_EGENERIC;
1554 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1558 "unable to render text FT_Get_Glyph returned %d", i_error );
1559 p_line->pp_glyphs[ i ] = NULL;
1560 return VLC_EGENERIC;
1562 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1563 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1566 FT_Done_Glyph( tmp_glyph );
1569 if( b_uline || b_through )
1571 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1572 p_face->size->metrics.y_scale));
1573 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1574 p_face->size->metrics.y_scale));
1576 p_line->pi_underline_offset[ i ] =
1577 ( aOffset < 0 ) ? -aOffset : aOffset;
1578 p_line->pi_underline_thickness[ i ] =
1579 ( aSize < 0 ) ? -aSize : aSize;
1582 /* Move the baseline to make it strikethrough instead of
1583 * underline. That means that strikethrough takes precedence
1585 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1586 p_face->size->metrics.y_scale));
1588 p_line->pi_underline_offset[ i ] -=
1589 ( aDescent < 0 ) ? -aDescent : aDescent;
1593 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1594 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1595 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1596 p_line->p_fg_bg_ratio[ i ] = 0x00;
1598 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1599 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1600 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1602 for( ; i >= *pi_start; i-- )
1603 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1606 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1610 if( psz_unicode == psz_unicode_start )
1612 if( b_first_on_line )
1614 msg_Warn( p_filter, "unbreakable string" );
1615 p_line->pp_glyphs[ i ] = NULL;
1616 return VLC_EGENERIC;
1618 *pi_pen_x = i_pen_x_start;
1620 p_line->i_width = line.xMax;
1621 p_line->i_height = __MAX( p_line->i_height,
1622 p_face->size->metrics.height >> 6 );
1623 p_line->pp_glyphs[ i ] = NULL;
1625 p_result->x = __MAX( p_result->x, line.xMax );
1626 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1627 i_yMax - i_yMin ) );
1632 *psz_unicode = '\n';
1634 psz_unicode = psz_unicode_start;
1635 *pi_pen_x = i_pen_x_start;
1643 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1644 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1646 i_previous = i_glyph_index;
1647 *pi_pen_x += p_face->glyph->advance.x >> 6;
1650 p_line->i_width = line.xMax;
1651 p_line->i_height = __MAX( p_line->i_height,
1652 p_face->size->metrics.height >> 6 );
1653 p_line->pp_glyphs[ i ] = NULL;
1655 p_result->x = __MAX( p_result->x, line.xMax );
1656 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1657 line.yMax - line.yMin ) );
1661 /* Get rid of any text processed - if necessary repositioning
1662 * at the start of a new line of text
1666 *psz_unicode_start = '\0';
1668 else if( psz_unicode > psz_unicode_start )
1670 for( i=0; psz_unicode[ i ]; i++ )
1671 psz_unicode_start[ i ] = psz_unicode[ i ];
1672 psz_unicode_start[ i ] = '\0';
1678 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1679 uint32_t **psz_text_out, uint32_t *pi_runs,
1680 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1681 ft_style_t *p_style )
1683 uint32_t i_string_length = 0;
1685 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1686 *psz_text_out += i_string_length;
1688 if( ppp_styles && ppi_run_lengths )
1692 /* XXX this logic looks somewhat broken */
1696 *ppp_styles = realloc_or_free( *ppp_styles,
1697 *pi_runs * sizeof( ft_style_t * ) );
1699 else if( *pi_runs == 1 )
1701 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1704 /* We have just malloc'ed this memory successfully -
1705 * *pi_runs HAS to be within the memory area of *ppp_styles */
1708 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1712 /* XXX more iffy logic */
1714 if( *ppi_run_lengths )
1716 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1717 *pi_runs * sizeof( uint32_t ) );
1719 else if( *pi_runs == 1 )
1721 *ppi_run_lengths = (uint32_t *)
1722 malloc( *pi_runs * sizeof( uint32_t ) );
1725 /* same remarks here */
1726 if( *ppi_run_lengths )
1728 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1731 /* If we couldn't use the p_style argument due to memory allocation
1732 * problems above, release it here.
1734 if( p_style ) DeleteStyle( p_style );
1737 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1741 for( k=0; k < p_sys->i_font_attachments; k++ )
1743 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1745 FT_Face p_face = NULL;
1747 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1755 bool match = !strcasecmp( p_face->family_name,
1756 p_style->psz_fontname );
1758 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1759 match = match && p_style->b_bold;
1761 match = match && !p_style->b_bold;
1763 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1764 match = match && p_style->b_italic;
1766 match = match && !p_style->b_italic;
1774 FT_Done_Face( p_face );
1779 return VLC_EGENERIC;
1782 static int ProcessLines( filter_t *p_filter,
1787 uint32_t *pi_run_lengths,
1788 ft_style_t **pp_styles,
1789 line_desc_t **pp_lines,
1791 FT_Vector *p_result,
1795 uint32_t *pi_k_run_lengths,
1796 uint32_t *pi_k_durations )
1798 filter_sys_t *p_sys = p_filter->p_sys;
1799 ft_style_t **pp_char_styles;
1800 int *p_new_positions = NULL;
1801 int8_t *p_levels = NULL;
1802 uint8_t *pi_karaoke_bar = NULL;
1806 /* Assign each character in the text string its style explicitly, so that
1807 * after the characters have been shuffled around by Fribidi, we can re-apply
1808 * the styles, and to simplify the calculation of runs within a line.
1810 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1811 if( !pp_char_styles )
1816 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1817 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1818 * we just won't be able to display the progress bar; at least we'll
1824 for( j = 0; j < i_runs; j++ )
1825 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1826 pp_char_styles[ i++ ] = pp_styles[ j ];
1828 #if defined(HAVE_FRIBIDI)
1830 ft_style_t **pp_char_styles_new;
1831 int *p_old_positions;
1832 uint32_t *p_fribidi_string;
1833 int start_pos, pos = 0;
1835 pp_char_styles_new = (ft_style_t **)
1836 malloc( i_len * sizeof( ft_style_t * ));
1838 p_fribidi_string = (uint32_t *)
1839 malloc( (i_len + 1) * sizeof(uint32_t) );
1840 p_old_positions = (int *)
1841 malloc( (i_len + 1) * sizeof( int ) );
1842 p_new_positions = (int *)
1843 malloc( (i_len + 1) * sizeof( int ) );
1844 p_levels = (int8_t *)
1845 malloc( (i_len + 1) * sizeof( int8_t ) );
1847 if( ! pp_char_styles_new ||
1848 ! p_fribidi_string ||
1849 ! p_old_positions ||
1850 ! p_new_positions ||
1854 free( p_old_positions );
1855 free( p_new_positions );
1856 free( p_fribidi_string );
1857 free( pp_char_styles_new );
1858 free( pi_karaoke_bar );
1860 free( pp_char_styles );
1864 /* Do bidi conversion line-by-line */
1867 while(pos < i_len) {
1868 if (psz_text[pos] != '\n')
1870 p_fribidi_string[pos] = psz_text[pos];
1871 pp_char_styles_new[pos] = pp_char_styles[pos];
1872 p_new_positions[pos] = pos;
1877 while(pos < i_len) {
1878 if (psz_text[pos] == '\n')
1882 if (pos > start_pos)
1884 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1885 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1886 pos - start_pos, &base_dir,
1887 (FriBidiChar*)p_fribidi_string + start_pos,
1888 p_new_positions + start_pos,
1890 p_levels + start_pos );
1891 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1893 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1894 p_old_positions[ j - start_pos ] ];
1895 p_new_positions[ j ] += start_pos;
1899 free( p_old_positions );
1900 free( pp_char_styles );
1901 pp_char_styles = pp_char_styles_new;
1902 psz_text = p_fribidi_string;
1903 p_fribidi_string[ i_len ] = 0;
1906 /* Work out the karaoke */
1907 if( pi_karaoke_bar )
1909 int64_t i_last_duration = 0;
1910 int64_t i_duration = 0;
1911 int64_t i_start_pos = 0;
1912 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1914 for( k = 0; k< i_k_runs; k++ )
1916 double fraction = 0.0;
1918 i_duration += pi_k_durations[ k ];
1920 if( i_duration < i_elapsed )
1922 /* Completely finished this run-length -
1923 * let it render normally */
1927 else if( i_elapsed < i_last_duration )
1929 /* Haven't got up to this segment yet -
1930 * render it completely in karaoke BG mode */
1936 /* Partway through this run */
1938 fraction = (double)(i_elapsed - i_last_duration) /
1939 (double)pi_k_durations[ k ];
1941 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1943 double shade = pi_k_run_lengths[ k ] * fraction;
1945 if( p_new_positions )
1946 j = p_new_positions[ i_start_pos + i ];
1948 j = i_start_pos + i;
1950 if( i < (uint32_t)shade )
1951 pi_karaoke_bar[ j ] = 0xff;
1952 else if( (double)i > shade )
1953 pi_karaoke_bar[ j ] = 0x00;
1956 shade -= (int)shade;
1957 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1958 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1962 i_last_duration = i_duration;
1963 i_start_pos += pi_k_run_lengths[ k ];
1967 free( p_new_positions );
1969 FT_Vector tmp_result;
1971 line_desc_t *p_line = NULL;
1972 line_desc_t *p_prev = NULL;
1978 p_result->x = p_result->y = 0;
1979 tmp_result.x = tmp_result.y = 0;
1982 for( k = 0; k <= (uint32_t) i_len; k++ )
1984 if( ( k == (uint32_t) i_len ) ||
1986 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1988 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1990 /* End of the current style run */
1991 FT_Face p_face = NULL;
1994 /* Look for a match amongst our attachments first */
1995 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1999 char *psz_fontfile = NULL;
2001 psz_fontfile = FontConfig_Select( NULL,
2002 p_style->psz_fontname,
2006 if( psz_fontfile && ! *psz_fontfile )
2008 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2009 " so using default font", p_style->psz_fontname,
2010 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2011 (p_style->b_bold ? "(Bold)" :
2012 (p_style->b_italic ? "(Italic)" : ""))) );
2013 free( psz_fontfile );
2014 psz_fontfile = NULL;
2019 if( FT_New_Face( p_sys->p_library,
2020 psz_fontfile, i_idx, &p_face ) )
2022 free( psz_fontfile );
2023 free( pp_char_styles );
2024 #if defined(HAVE_FRIBIDI)
2027 free( pi_karaoke_bar );
2028 return VLC_EGENERIC;
2030 free( psz_fontfile );
2034 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2036 /* We've loaded a font face which is unhelpful for actually
2037 * rendering text - fallback to the default one.
2039 FT_Done_Face( p_face );
2043 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2044 ft_encoding_unicode ) ||
2045 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2046 p_style->i_font_size ) )
2048 if( p_face ) FT_Done_Face( p_face );
2049 free( pp_char_styles );
2050 #if defined(HAVE_FRIBIDI)
2053 free( pi_karaoke_bar );
2054 return VLC_EGENERIC;
2056 p_sys->i_use_kerning =
2057 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2060 uint32_t *psz_unicode = (uint32_t *)
2061 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2064 if( p_face ) FT_Done_Face( p_face );
2065 free( pp_char_styles );
2066 free( psz_unicode );
2067 #if defined(HAVE_FRIBIDI)
2070 free( pi_karaoke_bar );
2073 memcpy( psz_unicode, psz_text + i_prev,
2074 sizeof( uint32_t ) * ( k - i_prev ) );
2075 psz_unicode[ k - i_prev ] = 0;
2076 while( *psz_unicode )
2080 if( !(p_line = NewLine( i_len - i_prev)) )
2082 if( p_face ) FT_Done_Face( p_face );
2083 free( pp_char_styles );
2084 free( psz_unicode );
2085 #if defined(HAVE_FRIBIDI)
2088 free( pi_karaoke_bar );
2091 /* New Color mode only works in YUVA rendering mode --
2092 * (RGB mode has palette constraints on it). We therefore
2093 * need to populate the legacy colour fields also.
2095 p_line->b_new_color_mode = true;
2096 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2097 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2098 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2099 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2100 p_line->p_next = NULL;
2102 i_pen_y += tmp_result.y;
2106 if( p_prev ) p_prev->p_next = p_line;
2107 else *pp_lines = p_line;
2110 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2111 p_style->i_font_color, p_style->b_underline,
2113 p_style->i_karaoke_bg_color,
2114 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2115 &tmp_result ) != VLC_SUCCESS )
2117 if( p_face ) FT_Done_Face( p_face );
2118 free( pp_char_styles );
2119 free( psz_unicode );
2120 #if defined(HAVE_FRIBIDI)
2123 free( pi_karaoke_bar );
2124 return VLC_EGENERIC;
2129 p_result->x = __MAX( p_result->x, tmp_result.x );
2130 p_result->y += tmp_result.y;
2135 if( *psz_unicode == '\n')
2139 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2141 *c_ptr = *(c_ptr+1);
2146 free( psz_unicode );
2147 if( p_face ) FT_Done_Face( p_face );
2151 free( pp_char_styles );
2152 #if defined(HAVE_FRIBIDI)
2157 p_result->x = __MAX( p_result->x, tmp_result.x );
2158 p_result->y += tmp_result.y;
2161 if( pi_karaoke_bar )
2164 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2166 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2168 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2172 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2174 /* 100% BG colour will render faster if we
2175 * instead make it 100% FG colour, so leave
2176 * the ratio alone and copy the value across
2178 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2182 if( pi_karaoke_bar[ i ] & 0x80 )
2184 /* Swap Left and Right sides over for Right aligned
2185 * language text (eg. Arabic, Hebrew)
2187 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2189 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2190 p_line->p_bg_rgb[ k ] = i_tmp;
2192 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2195 /* Jump over the '\n' at the line-end */
2198 free( pi_karaoke_bar );
2204 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2205 subpicture_region_t *p_region_in )
2207 int rv = VLC_SUCCESS;
2208 stream_t *p_sub = NULL;
2209 xml_reader_t *p_xml_reader = NULL;
2211 if( !p_region_in || !p_region_in->psz_html )
2212 return VLC_EGENERIC;
2214 /* Reset the default fontsize in case screen metrics have changed */
2215 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2217 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2218 (uint8_t *) p_region_in->psz_html,
2219 strlen( p_region_in->psz_html ),
2223 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2224 if( p_filter->p_sys->p_xml )
2226 bool b_karaoke = false;
2228 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2231 /* Look for Root Node */
2232 if( xml_ReaderRead( p_xml_reader ) == 1 )
2234 char *psz_node = xml_ReaderName( p_xml_reader );
2236 if( !strcasecmp( "karaoke", psz_node ) )
2238 /* We're going to have to render the text a number
2239 * of times to show the progress marker on the text.
2241 var_SetBool( p_filter, "text-rerender", true );
2244 else if( !strcasecmp( "text", psz_node ) )
2250 /* Only text and karaoke tags are supported */
2251 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2252 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2253 p_xml_reader = NULL;
2265 uint32_t i_runs = 0;
2266 uint32_t i_k_runs = 0;
2267 uint32_t *pi_run_lengths = NULL;
2268 uint32_t *pi_k_run_lengths = NULL;
2269 uint32_t *pi_k_durations = NULL;
2270 ft_style_t **pp_styles = NULL;
2272 line_desc_t *p_lines = NULL;
2274 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2275 sizeof( uint32_t ) );
2280 rv = ProcessNodes( p_filter, p_xml_reader,
2281 p_region_in->p_style, psz_text, &i_len,
2282 &i_runs, &pi_run_lengths, &pp_styles,
2284 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2287 p_region_out->i_x = p_region_in->i_x;
2288 p_region_out->i_y = p_region_in->i_y;
2290 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2292 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2293 pi_run_lengths, pp_styles, &p_lines, &result,
2294 b_karaoke, i_k_runs, pi_k_run_lengths,
2298 for( k=0; k<i_runs; k++)
2299 DeleteStyle( pp_styles[k] );
2301 free( pi_run_lengths );
2304 /* Don't attempt to render text that couldn't be layed out
2307 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2309 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2311 Render( p_filter, p_region_out, p_lines,
2312 result.x, result.y );
2316 RenderYUVA( p_filter, p_region_out, p_lines,
2317 result.x, result.y );
2321 FreeLines( p_lines );
2323 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2326 stream_Delete( p_sub );
2332 static char* FontConfig_Select( FcConfig* priv, const char* family,
2333 bool b_bold, bool b_italic, int *i_idx )
2336 FcPattern *pat, *p_pat;
2340 pat = FcPatternCreate();
2341 if (!pat) return NULL;
2343 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2344 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2345 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2346 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2348 FcDefaultSubstitute( pat );
2350 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2352 FcPatternDestroy( pat );
2356 p_pat = FcFontMatch( priv, pat, &result );
2357 FcPatternDestroy( pat );
2358 if( !p_pat ) return NULL;
2360 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2361 || ( val_b != FcTrue ) )
2363 FcPatternDestroy( p_pat );
2366 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2371 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2373 FcPatternDestroy( p_pat );
2378 if( strcasecmp((const char*)val_s, family ) != 0 )
2379 msg_Warn( p_filter, "fontconfig: selected font family is not"
2380 "the requested one: '%s' != '%s'\n",
2381 (const char*)val_s, family );
2384 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2386 FcPatternDestroy( p_pat );
2390 FcPatternDestroy( p_pat );
2391 return strdup( (const char*)val_s );
2395 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2396 uint32_t **psz_text_out, uint32_t *pi_runs,
2397 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2398 ft_style_t *p_style )
2400 VLC_UNUSED(p_filter);
2401 VLC_UNUSED(psz_text_in);
2402 VLC_UNUSED(psz_text_out);
2403 VLC_UNUSED(pi_runs);
2404 VLC_UNUSED(ppi_run_lengths);
2405 VLC_UNUSED(ppp_styles);
2406 VLC_UNUSED(p_style);
2409 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2410 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2411 bool b_uline, bool b_through )
2414 VLC_UNUSED(p_fonts);
2416 VLC_UNUSED(b_italic);
2417 VLC_UNUSED(b_uline);
2418 VLC_UNUSED(b_through);
2423 static void FreeLine( line_desc_t *p_line )
2426 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2428 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2430 free( p_line->pp_glyphs );
2431 free( p_line->p_glyph_pos );
2432 free( p_line->p_fg_rgb );
2433 free( p_line->p_bg_rgb );
2434 free( p_line->p_fg_bg_ratio );
2435 free( p_line->pi_underline_offset );
2436 free( p_line->pi_underline_thickness );
2440 static void FreeLines( line_desc_t *p_lines )
2442 line_desc_t *p_line, *p_next;
2444 if( !p_lines ) return;
2446 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2448 p_next = p_line->p_next;
2453 static line_desc_t *NewLine( int i_count )
2455 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2457 if( !p_line ) return NULL;
2458 p_line->i_height = 0;
2459 p_line->i_width = 0;
2460 p_line->p_next = NULL;
2462 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2463 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2464 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2465 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2466 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2467 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2468 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2469 if( ( p_line->pp_glyphs == NULL ) ||
2470 ( p_line->p_glyph_pos == NULL ) ||
2471 ( p_line->p_fg_rgb == NULL ) ||
2472 ( p_line->p_bg_rgb == NULL ) ||
2473 ( p_line->p_fg_bg_ratio == NULL ) ||
2474 ( p_line->pi_underline_offset == NULL ) ||
2475 ( p_line->pi_underline_thickness == NULL ) )
2477 free( p_line->pi_underline_thickness );
2478 free( p_line->pi_underline_offset );
2479 free( p_line->p_fg_rgb );
2480 free( p_line->p_bg_rgb );
2481 free( p_line->p_fg_bg_ratio );
2482 free( p_line->p_glyph_pos );
2483 free( p_line->pp_glyphs );
2487 p_line->pp_glyphs[0] = NULL;
2488 p_line->b_new_color_mode = false;
2493 static int GetFontSize( filter_t *p_filter )
2495 filter_sys_t *p_sys = p_filter->p_sys;
2499 if( p_sys->i_default_font_size )
2501 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2502 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2504 i_size = p_sys->i_default_font_size;
2508 var_Get( p_filter, "freetype-rel-fontsize", &val );
2511 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2512 p_filter->p_sys->i_display_height =
2513 p_filter->fmt_out.video.i_height;
2518 msg_Warn( p_filter, "invalid fontsize, using 12" );
2519 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2520 i_size = 12 * val.i_int / 1000;
2527 static int SetFontSize( filter_t *p_filter, int i_size )
2529 filter_sys_t *p_sys = p_filter->p_sys;
2533 i_size = GetFontSize( p_filter );
2535 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2538 p_sys->i_font_size = i_size;
2540 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2542 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2543 return VLC_EGENERIC;
2549 static void YUVFromRGB( uint32_t i_argb,
2550 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2552 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2553 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2554 int i_blue = ( i_argb & 0x000000ff );
2556 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2557 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2558 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2559 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2560 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2561 -585 * i_blue + 4096 + 1048576) >> 13, 240);