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>
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 uint16_t *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 */
243 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
244 static void FreeLines( line_desc_t * );
245 static void FreeLine( line_desc_t * );
247 /*****************************************************************************
248 * filter_sys_t: freetype local data
249 *****************************************************************************
250 * This structure is part of the video output thread descriptor.
251 * It describes the freetype specific properties of an output thread.
252 *****************************************************************************/
255 FT_Library p_library; /* handle to library */
256 FT_Face p_face; /* handle to face object */
258 uint8_t i_font_opacity;
263 int i_default_font_size;
264 int i_display_height;
265 #ifdef HAVE_FONTCONFIG
266 char* psz_fontfamily;
270 input_attachment_t **pp_font_attachments;
271 int i_font_attachments;
275 #define UCHAR uint32_t
276 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
277 #define TR_FONT_STYLE_PTR ft_style_t *
279 #include "text_renderer.h"
281 /*****************************************************************************
282 * Create: allocates osd-text video thread output method
283 *****************************************************************************
284 * This function allocates and initializes a Clone vout method.
285 *****************************************************************************/
286 static int Create( vlc_object_t *p_this )
288 filter_t *p_filter = (filter_t *)p_this;
290 char *psz_fontfile=NULL;
291 char *psz_fontfamily=NULL;
292 int i_error,fontindex;
294 #ifdef HAVE_FONTCONFIG
295 FcPattern *fontpattern = NULL, *fontmatch = NULL;
296 /* Initialise result to Match, as fontconfig doesnt
297 * really set this other than some error-cases */
298 FcResult fontresult = FcResultMatch;
302 /* Allocate structure */
303 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
306 #ifdef HAVE_FONTCONFIG
307 p_sys->psz_fontfamily = NULL;
311 p_sys->p_library = 0;
312 p_sys->i_font_size = 0;
313 p_sys->i_display_height = 0;
315 var_Create( p_filter, "freetype-rel-fontsize",
316 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
318 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
319 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
320 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
321 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
322 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
323 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
324 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
327 if( !psz_fontfamily || !*psz_fontfamily )
329 free( psz_fontfamily );
330 #ifdef HAVE_FONTCONFIG
331 psz_fontfamily=strdup( DEFAULT_FONT );
333 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
334 if( !psz_fontfamily )
337 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
338 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
340 strcpy( psz_fontfamily, DEFAULT_FONT );
342 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
346 #ifdef HAVE_FONTCONFIG
347 /* Lets find some fontfile from freetype-font variable family */
349 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
353 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
354 _("Building font cache"),
355 _("Please wait while your font cache is rebuilt.\n"
356 "This should take less than few minutes."), NULL );
358 path = (char *)malloc( 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, psz_fontfile );
431 p_sys->psz_fontfamily = strdup( psz_fontfamily );
435 dialog_ProgressSet( p_dialog, NULL, 1.0 );
436 dialog_ProgressDestroy( p_dialog );
442 #ifdef HAVE_FONTCONFIG
443 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
444 psz_fontfile = psz_fontfamily;
449 i_error = FT_Init_FreeType( &p_sys->p_library );
452 msg_Err( p_filter, "couldn't initialize freetype" );
456 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
457 fontindex, &p_sys->p_face );
459 if( i_error == FT_Err_Unknown_File_Format )
461 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
466 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
470 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
473 msg_Err( p_filter, "font has no unicode translation table" );
477 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
479 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
482 p_sys->pp_font_attachments = NULL;
483 p_sys->i_font_attachments = 0;
485 p_filter->pf_render_text = RenderText;
486 #ifdef HAVE_FONTCONFIG
487 p_filter->pf_render_html = RenderHtml;
488 FcPatternDestroy( fontmatch );
489 FcPatternDestroy( fontpattern );
491 p_filter->pf_render_html = NULL;
494 free( psz_fontfamily );
495 LoadFontsFromAttachments( p_filter );
500 #ifdef HAVE_FONTCONFIG
501 if( fontmatch ) FcPatternDestroy( fontmatch );
502 if( fontpattern ) FcPatternDestroy( fontpattern );
504 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
505 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
506 free( psz_fontfamily );
511 /*****************************************************************************
512 * Destroy: destroy Clone video thread output method
513 *****************************************************************************
514 * Clean up all data and library connections
515 *****************************************************************************/
516 static void Destroy( vlc_object_t *p_this )
518 filter_t *p_filter = (filter_t *)p_this;
519 filter_sys_t *p_sys = p_filter->p_sys;
521 if( p_sys->pp_font_attachments )
525 for( k = 0; k < p_sys->i_font_attachments; k++ )
526 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
528 free( p_sys->pp_font_attachments );
531 #ifdef HAVE_FONTCONFIG
532 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
533 free( p_sys->psz_fontfamily );
536 /* FcFini asserts calling the subfunction FcCacheFini()
537 * even if no other library functions have been made since FcInit(),
538 * so don't call it. */
540 FT_Done_Face( p_sys->p_face );
541 FT_Done_FreeType( p_sys->p_library );
545 /*****************************************************************************
546 * Make any TTF/OTF fonts present in the attachments of the media file
547 * and store them for later use by the FreeType Engine
548 *****************************************************************************/
549 static int LoadFontsFromAttachments( filter_t *p_filter )
551 filter_sys_t *p_sys = p_filter->p_sys;
552 input_thread_t *p_input;
553 input_attachment_t **pp_attachments;
554 int i_attachments_cnt;
556 int rv = VLC_SUCCESS;
558 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
562 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
564 vlc_object_release(p_input);
568 p_sys->i_font_attachments = 0;
569 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
570 if(! p_sys->pp_font_attachments )
573 for( k = 0; k < i_attachments_cnt; k++ )
575 input_attachment_t *p_attach = pp_attachments[k];
577 if( p_sys->pp_font_attachments )
579 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
580 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
581 ( p_attach->i_data > 0 ) &&
582 ( p_attach->p_data != NULL ) )
584 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
588 vlc_input_attachment_Delete( p_attach );
593 vlc_input_attachment_Delete( p_attach );
596 free( pp_attachments );
598 vlc_object_release(p_input);
603 /*****************************************************************************
604 * Render: place string in picture
605 *****************************************************************************
606 * This function merges the previously rendered freetype glyphs into a picture
607 *****************************************************************************/
608 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
609 line_desc_t *p_line, int i_width, int i_height )
611 VLC_UNUSED(p_filter);
612 static const uint8_t pi_gamma[16] =
613 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
614 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
618 int i, x, y, i_pitch;
619 uint8_t i_y; /* YUV values, derived from incoming RGB */
622 /* Create a new subpicture region */
623 memset( &fmt, 0, sizeof(video_format_t) );
624 fmt.i_chroma = VLC_CODEC_YUVP;
626 fmt.i_width = fmt.i_visible_width = i_width + 4;
627 fmt.i_height = fmt.i_visible_height = i_height + 4;
628 if( p_region->fmt.i_visible_width > 0 )
629 fmt.i_visible_width = p_region->fmt.i_visible_width;
630 if( p_region->fmt.i_visible_height > 0 )
631 fmt.i_visible_height = p_region->fmt.i_visible_height;
632 fmt.i_x_offset = fmt.i_y_offset = 0;
634 assert( !p_region->p_picture );
635 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
636 if( !p_region->p_picture )
640 /* Calculate text color components */
641 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
642 25 * p_line->i_blue + 128) >> 8) + 16;
643 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
644 112 * p_line->i_blue + 128) >> 8) + 128;
645 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
646 18 * p_line->i_blue + 128) >> 8) + 128;
649 fmt.p_palette->i_entries = 16;
650 for( i = 0; i < 8; i++ )
652 fmt.p_palette->palette[i][0] = 0;
653 fmt.p_palette->palette[i][1] = 0x80;
654 fmt.p_palette->palette[i][2] = 0x80;
655 fmt.p_palette->palette[i][3] = pi_gamma[i];
656 fmt.p_palette->palette[i][3] =
657 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
659 for( i = 8; i < fmt.p_palette->i_entries; i++ )
661 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
662 fmt.p_palette->palette[i][1] = i_u;
663 fmt.p_palette->palette[i][2] = i_v;
664 fmt.p_palette->palette[i][3] = pi_gamma[i];
665 fmt.p_palette->palette[i][3] =
666 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
669 p_dst = p_region->p_picture->Y_PIXELS;
670 i_pitch = p_region->p_picture->Y_PITCH;
672 /* Initialize the region pixels */
673 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
675 for( ; p_line != NULL; p_line = p_line->p_next )
677 int i_glyph_tmax = 0;
678 int i_bitmap_offset, i_offset, i_align_offset = 0;
679 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
681 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
682 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
685 if( p_line->i_width < i_width )
687 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
689 i_align_offset = i_width - p_line->i_width;
691 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
693 i_align_offset = ( i_width - p_line->i_width ) / 2;
697 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
699 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
701 i_offset = ( p_line->p_glyph_pos[ i ].y +
702 i_glyph_tmax - p_glyph->top + 2 ) *
703 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
706 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
708 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
710 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
712 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
719 /* Outlining (find something better than nearest neighbour filtering ?) */
722 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
723 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
724 uint8_t left, current;
726 for( y = 1; y < (int)fmt.i_height - 1; y++ )
728 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
729 p_dst += p_region->p_picture->Y_PITCH;
732 for( x = 1; x < (int)fmt.i_width - 1; x++ )
735 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
736 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;
740 memset( p_top, 0, fmt.i_width );
746 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
747 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
748 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
749 int i_glyph_tmax, int i_align_offset,
750 uint8_t i_y, uint8_t i_u, uint8_t i_v,
751 subpicture_region_t *p_region)
755 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
757 p_dst_y = p_region->p_picture->Y_PIXELS;
758 p_dst_u = p_region->p_picture->U_PIXELS;
759 p_dst_v = p_region->p_picture->V_PIXELS;
760 p_dst_a = p_region->p_picture->A_PIXELS;
761 i_pitch = p_region->p_picture->A_PITCH;
763 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
764 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
766 for( y = 0; y < i_line_thickness; y++ )
768 int i_extra = p_this_glyph->bitmap.width;
772 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
773 (p_this_glyph_pos->x + p_this_glyph->left);
775 for( x = 0; x < i_extra; x++ )
779 /* break the underline around the tails of any glyphs which cross it */
780 for( z = x - i_line_thickness;
781 z < x + i_line_thickness && b_ok;
784 if( p_next_glyph && ( z >= i_extra ) )
786 int i_row = i_line_offset + p_next_glyph->top + y;
788 if( ( p_next_glyph->bitmap.rows > i_row ) &&
789 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
794 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
796 int i_row = i_line_offset + p_this_glyph->top + y;
798 if( ( p_this_glyph->bitmap.rows > i_row ) &&
799 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
808 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
809 p_dst_u[i_offset+x] = i_u;
810 p_dst_v[i_offset+x] = i_v;
811 p_dst_a[i_offset+x] = 255;
818 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
820 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
821 int i_pitch = p_region->p_picture->A_PITCH;
824 for( ; p_line != NULL; p_line = p_line->p_next )
826 int i_glyph_tmax=0, i = 0;
827 int i_bitmap_offset, i_offset, i_align_offset = 0;
828 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
830 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
831 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
834 if( p_line->i_width < i_width )
836 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
838 i_align_offset = i_width - p_line->i_width;
840 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
842 i_align_offset = ( i_width - p_line->i_width ) / 2;
846 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
848 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
850 i_offset = ( p_line->p_glyph_pos[ i ].y +
851 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
852 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
853 i_align_offset +xoffset;
855 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
857 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
859 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
860 if( p_dst[i_offset+x] <
861 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
863 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
872 /*****************************************************************************
873 * Render: place string in picture
874 *****************************************************************************
875 * This function merges the previously rendered freetype glyphs into a picture
876 *****************************************************************************/
877 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
878 line_desc_t *p_line, int i_width, int i_height )
880 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
882 int i, x, y, i_pitch, i_alpha;
883 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
885 if( i_width == 0 || i_height == 0 )
888 /* Create a new subpicture region */
889 memset( &fmt, 0, sizeof(video_format_t) );
890 fmt.i_chroma = VLC_CODEC_YUVA;
892 fmt.i_width = fmt.i_visible_width = i_width + 6;
893 fmt.i_height = fmt.i_visible_height = i_height + 6;
894 if( p_region->fmt.i_visible_width > 0 )
895 fmt.i_visible_width = p_region->fmt.i_visible_width;
896 if( p_region->fmt.i_visible_height > 0 )
897 fmt.i_visible_height = p_region->fmt.i_visible_height;
898 fmt.i_x_offset = fmt.i_y_offset = 0;
900 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
901 if( !p_region->p_picture )
905 /* Calculate text color components */
906 YUVFromRGB( (p_line->i_red << 16) |
907 (p_line->i_green << 8) |
910 i_alpha = p_line->i_alpha;
912 p_dst_y = p_region->p_picture->Y_PIXELS;
913 p_dst_u = p_region->p_picture->U_PIXELS;
914 p_dst_v = p_region->p_picture->V_PIXELS;
915 p_dst_a = p_region->p_picture->A_PIXELS;
916 i_pitch = p_region->p_picture->A_PITCH;
918 /* Initialize the region pixels */
919 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
921 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
922 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
923 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
924 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
928 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
929 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
930 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
931 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
933 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
934 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
936 DrawBlack( p_line, i_width, p_region, 0, 0);
937 DrawBlack( p_line, i_width, p_region, -1, 0);
938 DrawBlack( p_line, i_width, p_region, 0, -1);
939 DrawBlack( p_line, i_width, p_region, 1, 0);
940 DrawBlack( p_line, i_width, p_region, 0, 1);
943 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
945 DrawBlack( p_line, i_width, p_region, -1, -1);
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);
950 DrawBlack( p_line, i_width, p_region, -2, 0);
951 DrawBlack( p_line, i_width, p_region, 0, -2);
952 DrawBlack( p_line, i_width, p_region, 2, 0);
953 DrawBlack( p_line, i_width, p_region, 0, 2);
955 DrawBlack( p_line, i_width, p_region, -2, -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);
960 DrawBlack( p_line, i_width, p_region, -3, 0);
961 DrawBlack( p_line, i_width, p_region, 0, -3);
962 DrawBlack( p_line, i_width, p_region, 3, 0);
963 DrawBlack( p_line, i_width, p_region, 0, 3);
966 for( ; p_line != NULL; p_line = p_line->p_next )
968 int i_glyph_tmax = 0;
969 int i_bitmap_offset, i_offset, i_align_offset = 0;
970 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
972 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
973 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
976 if( p_line->i_width < i_width )
978 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
980 i_align_offset = i_width - p_line->i_width;
982 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
984 i_align_offset = ( i_width - p_line->i_width ) / 2;
988 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
990 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
992 i_offset = ( p_line->p_glyph_pos[ i ].y +
993 i_glyph_tmax - p_glyph->top + 3 ) *
994 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
997 if( p_line->b_new_color_mode )
999 /* Every glyph can (and in fact must) have its own color */
1000 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1003 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1005 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1007 uint8_t i_y_local = i_y;
1008 uint8_t i_u_local = i_u;
1009 uint8_t i_v_local = i_v;
1011 if( p_line->p_fg_bg_ratio != 0x00 )
1013 int i_split = p_glyph->bitmap.width *
1014 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1018 YUVFromRGB( p_line->p_bg_rgb[ i ],
1019 &i_y_local, &i_u_local, &i_v_local );
1023 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1025 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1026 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1028 p_dst_u[i_offset+x] = i_u;
1029 p_dst_v[i_offset+x] = i_v;
1031 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1032 p_dst_a[i_offset+x] = 0xff;
1035 i_offset += i_pitch;
1038 if( p_line->pi_underline_thickness[ i ] )
1040 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1041 p_line->pi_underline_offset[ i ],
1042 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1043 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1044 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1045 i_glyph_tmax, i_align_offset,
1052 /* Apply the alpha setting */
1053 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1054 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1060 * This function renders a text subpicture region into another one.
1061 * It also calculates the size needed for this string, and renders the
1062 * needed glyphs into memory. It is used as pf_add_string callback in
1063 * the vout method by this module
1065 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1066 subpicture_region_t *p_region_in )
1068 filter_sys_t *p_sys = p_filter->p_sys;
1069 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1070 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1071 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1072 int i_string_length;
1074 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1075 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1085 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1086 psz_string = p_region_in->psz_text;
1087 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1089 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1090 i_scale = val.i_int;
1092 if( p_region_in->p_style )
1094 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1095 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1096 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1100 i_font_color = p_sys->i_font_color;
1101 i_font_alpha = 255 - p_sys->i_font_opacity;
1102 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1105 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1106 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1107 SetFontSize( p_filter, i_font_size );
1109 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1110 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1111 i_blue = i_font_color & 0x000000FF;
1113 result.x = result.y = 0;
1114 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1116 psz_unicode = psz_unicode_orig =
1117 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1118 if( psz_unicode == NULL )
1120 #if defined(WORDS_BIGENDIAN)
1121 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1123 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1125 if( iconv_handle == (vlc_iconv_t)-1 )
1127 msg_Warn( p_filter, "unable to do conversion" );
1133 const char *p_in_buffer = psz_string;
1134 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1135 i_in_bytes = strlen( psz_string );
1136 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1137 i_out_bytes_left = i_out_bytes;
1138 p_out_buffer = (char *)psz_unicode;
1139 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1141 &p_out_buffer, &i_out_bytes_left );
1143 vlc_iconv_close( iconv_handle );
1147 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1148 "bytes left %u", (unsigned)i_in_bytes );
1151 *(uint32_t*)p_out_buffer = 0;
1152 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1155 #if defined(HAVE_FRIBIDI)
1157 uint32_t *p_fribidi_string;
1158 int32_t start_pos, pos = 0;
1160 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1161 if( !p_fribidi_string )
1164 /* Do bidi conversion line-by-line */
1165 while( pos < i_string_length )
1167 while( pos < i_string_length )
1169 i_char = psz_unicode[pos];
1170 if (i_char != '\r' && i_char != '\n')
1172 p_fribidi_string[pos] = i_char;
1176 while( pos < i_string_length )
1178 i_char = psz_unicode[pos];
1179 if (i_char == '\r' || i_char == '\n')
1183 if (pos > start_pos)
1185 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1186 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1189 (FriBidiChar*)p_fribidi_string + start_pos,
1194 free( psz_unicode_orig );
1195 psz_unicode = psz_unicode_orig = p_fribidi_string;
1196 p_fribidi_string[ i_string_length ] = 0;
1200 /* Calculate relative glyph positions and a bounding box for the
1202 if( !(p_line = NewLine( strlen( psz_string ))) )
1205 i_pen_x = i_pen_y = 0;
1207 psz_line_start = psz_unicode;
1209 #define face p_sys->p_face
1210 #define glyph face->glyph
1212 while( *psz_unicode )
1214 i_char = *psz_unicode++;
1215 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1220 if( i_char == '\n' )
1222 psz_line_start = psz_unicode;
1223 if( !(p_next = NewLine( strlen( psz_string ))) )
1225 p_line->p_next = p_next;
1226 p_line->i_width = line.xMax;
1227 p_line->i_height = face->size->metrics.height >> 6;
1228 p_line->pp_glyphs[ i ] = NULL;
1229 p_line->i_alpha = i_font_alpha;
1230 p_line->i_red = i_red;
1231 p_line->i_green = i_green;
1232 p_line->i_blue = i_blue;
1235 result.x = __MAX( result.x, line.xMax );
1236 result.y += face->size->metrics.height >> 6;
1239 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1240 i_pen_y += face->size->metrics.height >> 6;
1242 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1247 i_glyph_index = FT_Get_Char_Index( face, i_char );
1248 if( p_sys->i_use_kerning && i_glyph_index
1252 FT_Get_Kerning( face, i_previous, i_glyph_index,
1253 ft_kerning_default, &delta );
1254 i_pen_x += delta.x >> 6;
1257 p_line->p_glyph_pos[ i ].x = i_pen_x;
1258 p_line->p_glyph_pos[ i ].y = i_pen_y;
1259 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1262 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1266 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1269 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1273 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1274 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1277 FT_Done_Glyph( tmp_glyph );
1280 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1283 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1284 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1285 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1287 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1288 p_line->pp_glyphs[ i ] = NULL;
1290 p_line = NewLine( strlen( psz_string ));
1291 if( p_prev ) p_prev->p_next = p_line;
1292 else p_lines = p_line;
1294 uint32_t *psz_unicode_saved = psz_unicode;
1295 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1299 if( psz_unicode == psz_line_start )
1300 { /* try harder to break that line */
1301 psz_unicode = psz_unicode_saved;
1302 while( psz_unicode > psz_line_start &&
1303 *psz_unicode != '_' && *psz_unicode != '/' &&
1304 *psz_unicode != '\\' && *psz_unicode != '.' )
1309 if( psz_unicode == psz_line_start )
1311 msg_Warn( p_filter, "unbreakable string" );
1316 *psz_unicode = '\n';
1318 psz_unicode = psz_line_start;
1321 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1324 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1325 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1327 i_previous = i_glyph_index;
1328 i_pen_x += glyph->advance.x >> 6;
1332 p_line->i_width = line.xMax;
1333 p_line->i_height = face->size->metrics.height >> 6;
1334 p_line->pp_glyphs[ i ] = NULL;
1335 p_line->i_alpha = i_font_alpha;
1336 p_line->i_red = i_red;
1337 p_line->i_green = i_green;
1338 p_line->i_blue = i_blue;
1339 result.x = __MAX( result.x, line.xMax );
1340 result.y += line.yMax - line.yMin;
1345 p_region_out->i_x = p_region_in->i_x;
1346 p_region_out->i_y = p_region_in->i_y;
1348 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1349 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1351 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1353 free( psz_unicode_orig );
1354 FreeLines( p_lines );
1358 free( psz_unicode_orig );
1359 FreeLines( p_lines );
1360 return VLC_EGENERIC;
1363 #ifdef HAVE_FONTCONFIG
1364 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1365 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1366 bool b_italic, bool b_uline )
1368 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1372 p_style->i_font_size = i_font_size;
1373 p_style->i_font_color = i_font_color;
1374 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1375 p_style->b_italic = b_italic;
1376 p_style->b_bold = b_bold;
1377 p_style->b_underline = b_uline;
1379 p_style->psz_fontname = strdup( psz_fontname );
1384 static void DeleteStyle( ft_style_t *p_style )
1388 free( p_style->psz_fontname );
1393 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1400 if(( s1->i_font_size == s2->i_font_size ) &&
1401 ( s1->i_font_color == s2->i_font_color ) &&
1402 ( s1->b_italic == s2->b_italic ) &&
1403 ( s1->b_bold == s2->b_bold ) &&
1404 ( s1->b_underline == s2->b_underline ) &&
1405 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1412 static void IconvText( filter_t *p_filter, const char *psz_string,
1413 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1415 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1417 /* If memory hasn't been allocated for our output string, allocate it here
1418 * - the calling function must now be responsible for freeing it.
1420 if( !*ppsz_unicode )
1421 *ppsz_unicode = (uint32_t *)
1422 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1424 /* We don't need to handle a NULL pointer in *ppsz_unicode
1425 * if we are instead testing for a non NULL value like we are here */
1429 #if defined(WORDS_BIGENDIAN)
1430 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1432 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1434 if( iconv_handle != (vlc_iconv_t)-1 )
1436 char *p_in_buffer, *p_out_buffer;
1437 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1438 i_in_bytes = strlen( psz_string );
1439 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1440 i_out_bytes_left = i_out_bytes;
1441 p_in_buffer = (char *) psz_string;
1442 p_out_buffer = (char *) *ppsz_unicode;
1443 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1444 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1446 vlc_iconv_close( iconv_handle );
1450 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1451 "bytes left %u", (unsigned)i_in_bytes );
1455 *(uint32_t*)p_out_buffer = 0;
1457 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1462 msg_Warn( p_filter, "unable to do conversion" );
1467 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1468 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1471 ft_style_t *p_style = NULL;
1473 char *psz_fontname = NULL;
1474 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1475 uint32_t i_karaoke_bg_color = i_font_color;
1476 int i_font_size = p_sys->i_font_size;
1478 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1479 &i_font_color, &i_karaoke_bg_color ))
1481 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1482 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1487 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1488 bool b_uline, int i_karaoke_bgcolor,
1489 line_desc_t *p_line, uint32_t *psz_unicode,
1490 int *pi_pen_x, int i_pen_y, int *pi_start,
1491 FT_Vector *p_result )
1496 bool b_first_on_line = true;
1499 int i_pen_x_start = *pi_pen_x;
1501 uint32_t *psz_unicode_start = psz_unicode;
1503 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1505 /* Account for part of line already in position */
1506 for( i=0; i<*pi_start; i++ )
1510 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1511 ft_glyph_bbox_pixels, &glyph_size );
1513 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1514 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1515 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1516 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1522 b_first_on_line = false;
1524 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1530 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1531 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1535 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1536 ft_kerning_default, &delta );
1537 *pi_pen_x += delta.x >> 6;
1539 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1540 p_line->p_glyph_pos[ i ].y = i_pen_y;
1542 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1546 "unable to render text FT_Load_Glyph returned %d", i_error );
1547 p_line->pp_glyphs[ i ] = NULL;
1548 return VLC_EGENERIC;
1550 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1554 "unable to render text FT_Get_Glyph returned %d", i_error );
1555 p_line->pp_glyphs[ i ] = NULL;
1556 return VLC_EGENERIC;
1558 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1559 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1562 FT_Done_Glyph( tmp_glyph );
1567 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1568 p_face->size->metrics.y_scale));
1569 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1570 p_face->size->metrics.y_scale));
1572 p_line->pi_underline_offset[ i ] =
1573 ( aOffset < 0 ) ? -aOffset : aOffset;
1574 p_line->pi_underline_thickness[ i ] =
1575 ( aSize < 0 ) ? -aSize : aSize;
1577 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1578 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1579 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1580 p_line->p_fg_bg_ratio[ i ] = 0x00;
1582 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1583 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1584 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1586 for( ; i >= *pi_start; i-- )
1587 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1590 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1594 if( psz_unicode == psz_unicode_start )
1596 if( b_first_on_line )
1598 msg_Warn( p_filter, "unbreakable string" );
1599 p_line->pp_glyphs[ i ] = NULL;
1600 return VLC_EGENERIC;
1602 *pi_pen_x = i_pen_x_start;
1604 p_line->i_width = line.xMax;
1605 p_line->i_height = __MAX( p_line->i_height,
1606 p_face->size->metrics.height >> 6 );
1607 p_line->pp_glyphs[ i ] = NULL;
1609 p_result->x = __MAX( p_result->x, line.xMax );
1610 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1611 i_yMax - i_yMin ) );
1616 *psz_unicode = '\n';
1618 psz_unicode = psz_unicode_start;
1619 *pi_pen_x = i_pen_x_start;
1627 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1628 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1630 i_previous = i_glyph_index;
1631 *pi_pen_x += p_face->glyph->advance.x >> 6;
1634 p_line->i_width = line.xMax;
1635 p_line->i_height = __MAX( p_line->i_height,
1636 p_face->size->metrics.height >> 6 );
1637 p_line->pp_glyphs[ i ] = NULL;
1639 p_result->x = __MAX( p_result->x, line.xMax );
1640 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1641 line.yMax - line.yMin ) );
1645 /* Get rid of any text processed - if necessary repositioning
1646 * at the start of a new line of text
1650 *psz_unicode_start = '\0';
1652 else if( psz_unicode > psz_unicode_start )
1654 for( i=0; psz_unicode[ i ]; i++ )
1655 psz_unicode_start[ i ] = psz_unicode[ i ];
1656 psz_unicode_start[ i ] = '\0';
1662 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1663 uint32_t **psz_text_out, uint32_t *pi_runs,
1664 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1665 ft_style_t *p_style )
1667 uint32_t i_string_length = 0;
1669 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1670 *psz_text_out += i_string_length;
1672 if( ppp_styles && ppi_run_lengths )
1678 *ppp_styles = (ft_style_t **)
1679 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1681 else if( *pi_runs == 1 )
1683 *ppp_styles = (ft_style_t **)
1684 malloc( *pi_runs * sizeof( ft_style_t * ) );
1687 /* We have just malloc'ed this memory successfully -
1688 * *pi_runs HAS to be within the memory area of *ppp_styles */
1691 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1695 if( *ppi_run_lengths )
1697 *ppi_run_lengths = (uint32_t *)
1698 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1700 else if( *pi_runs == 1 )
1702 *ppi_run_lengths = (uint32_t *)
1703 malloc( *pi_runs * sizeof( uint32_t ) );
1706 /* same remarks here */
1707 if( *ppi_run_lengths )
1709 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1712 /* If we couldn't use the p_style argument due to memory allocation
1713 * problems above, release it here.
1715 if( p_style ) DeleteStyle( p_style );
1718 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1722 for( k=0; k < p_sys->i_font_attachments; k++ )
1724 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1726 FT_Face p_face = NULL;
1728 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1736 bool match = !strcasecmp( p_face->family_name,
1737 p_style->psz_fontname );
1739 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1740 match = match && p_style->b_bold;
1742 match = match && !p_style->b_bold;
1744 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1745 match = match && p_style->b_italic;
1747 match = match && !p_style->b_italic;
1755 FT_Done_Face( p_face );
1760 return VLC_EGENERIC;
1763 static int ProcessLines( filter_t *p_filter,
1768 uint32_t *pi_run_lengths,
1769 ft_style_t **pp_styles,
1770 line_desc_t **pp_lines,
1772 FT_Vector *p_result,
1776 uint32_t *pi_k_run_lengths,
1777 uint32_t *pi_k_durations )
1779 filter_sys_t *p_sys = p_filter->p_sys;
1780 ft_style_t **pp_char_styles;
1781 int *p_new_positions = NULL;
1782 int8_t *p_levels = NULL;
1783 uint8_t *pi_karaoke_bar = NULL;
1787 /* Assign each character in the text string its style explicitly, so that
1788 * after the characters have been shuffled around by Fribidi, we can re-apply
1789 * the styles, and to simplify the calculation of runs within a line.
1791 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1792 if( !pp_char_styles )
1797 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1798 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1799 * we just won't be able to display the progress bar; at least we'll
1805 for( j = 0; j < i_runs; j++ )
1806 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1807 pp_char_styles[ i++ ] = pp_styles[ j ];
1809 #if defined(HAVE_FRIBIDI)
1811 ft_style_t **pp_char_styles_new;
1812 int *p_old_positions;
1813 uint32_t *p_fribidi_string;
1814 int start_pos, pos = 0;
1816 pp_char_styles_new = (ft_style_t **)
1817 malloc( i_len * sizeof( ft_style_t * ));
1819 p_fribidi_string = (uint32_t *)
1820 malloc( (i_len + 1) * sizeof(uint32_t) );
1821 p_old_positions = (int *)
1822 malloc( (i_len + 1) * sizeof( int ) );
1823 p_new_positions = (int *)
1824 malloc( (i_len + 1) * sizeof( int ) );
1825 p_levels = (int8_t *)
1826 malloc( (i_len + 1) * sizeof( int8_t ) );
1828 if( ! pp_char_styles_new ||
1829 ! p_fribidi_string ||
1830 ! p_old_positions ||
1831 ! p_new_positions ||
1835 free( p_old_positions );
1836 free( p_new_positions );
1837 free( p_fribidi_string );
1838 free( pp_char_styles_new );
1839 free( pi_karaoke_bar );
1841 free( pp_char_styles );
1845 /* Do bidi conversion line-by-line */
1848 while(pos < i_len) {
1849 if (psz_text[pos] != '\n')
1851 p_fribidi_string[pos] = psz_text[pos];
1852 pp_char_styles_new[pos] = pp_char_styles[pos];
1853 p_new_positions[pos] = pos;
1858 while(pos < i_len) {
1859 if (psz_text[pos] == '\n')
1863 if (pos > start_pos)
1865 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1866 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1867 pos - start_pos, &base_dir,
1868 (FriBidiChar*)p_fribidi_string + start_pos,
1869 p_new_positions + start_pos,
1871 p_levels + start_pos );
1872 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1874 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1875 p_old_positions[ j - start_pos ] ];
1876 p_new_positions[ j ] += start_pos;
1880 free( p_old_positions );
1881 free( pp_char_styles );
1882 pp_char_styles = pp_char_styles_new;
1883 psz_text = p_fribidi_string;
1884 p_fribidi_string[ i_len ] = 0;
1887 /* Work out the karaoke */
1888 if( pi_karaoke_bar )
1890 int64_t i_last_duration = 0;
1891 int64_t i_duration = 0;
1892 int64_t i_start_pos = 0;
1893 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1895 for( k = 0; k< i_k_runs; k++ )
1897 double fraction = 0.0;
1899 i_duration += pi_k_durations[ k ];
1901 if( i_duration < i_elapsed )
1903 /* Completely finished this run-length -
1904 * let it render normally */
1908 else if( i_elapsed < i_last_duration )
1910 /* Haven't got up to this segment yet -
1911 * render it completely in karaoke BG mode */
1917 /* Partway through this run */
1919 fraction = (double)(i_elapsed - i_last_duration) /
1920 (double)pi_k_durations[ k ];
1922 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1924 double shade = pi_k_run_lengths[ k ] * fraction;
1926 if( p_new_positions )
1927 j = p_new_positions[ i_start_pos + i ];
1929 j = i_start_pos + i;
1931 if( i < (uint32_t)shade )
1932 pi_karaoke_bar[ j ] = 0xff;
1933 else if( (double)i > shade )
1934 pi_karaoke_bar[ j ] = 0x00;
1937 shade -= (int)shade;
1938 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1939 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1943 i_last_duration = i_duration;
1944 i_start_pos += pi_k_run_lengths[ k ];
1948 free( p_new_positions );
1950 FT_Vector tmp_result;
1952 line_desc_t *p_line = NULL;
1953 line_desc_t *p_prev = NULL;
1959 p_result->x = p_result->y = 0;
1960 tmp_result.x = tmp_result.y = 0;
1963 for( k = 0; k <= (uint32_t) i_len; k++ )
1965 if( ( k == (uint32_t) i_len ) ||
1967 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1969 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1971 /* End of the current style run */
1972 FT_Face p_face = NULL;
1975 /* Look for a match amongst our attachments first */
1976 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1980 char *psz_fontfile = NULL;
1982 psz_fontfile = FontConfig_Select( NULL,
1983 p_style->psz_fontname,
1987 if( psz_fontfile && ! *psz_fontfile )
1989 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1990 " so using default font", p_style->psz_fontname,
1991 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1992 (p_style->b_bold ? "(Bold)" :
1993 (p_style->b_italic ? "(Italic)" : ""))) );
1994 free( psz_fontfile );
1995 psz_fontfile = NULL;
2000 if( FT_New_Face( p_sys->p_library,
2001 psz_fontfile, i_idx, &p_face ) )
2003 free( psz_fontfile );
2004 free( pp_char_styles );
2005 #if defined(HAVE_FRIBIDI)
2008 free( pi_karaoke_bar );
2009 return VLC_EGENERIC;
2011 free( psz_fontfile );
2015 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2017 /* We've loaded a font face which is unhelpful for actually
2018 * rendering text - fallback to the default one.
2020 FT_Done_Face( p_face );
2024 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2025 ft_encoding_unicode ) ||
2026 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2027 p_style->i_font_size ) )
2029 if( p_face ) FT_Done_Face( p_face );
2030 free( pp_char_styles );
2031 #if defined(HAVE_FRIBIDI)
2034 free( pi_karaoke_bar );
2035 return VLC_EGENERIC;
2037 p_sys->i_use_kerning =
2038 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2041 uint32_t *psz_unicode = (uint32_t *)
2042 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2045 if( p_face ) FT_Done_Face( p_face );
2046 free( pp_char_styles );
2047 free( psz_unicode );
2048 #if defined(HAVE_FRIBIDI)
2051 free( pi_karaoke_bar );
2054 memcpy( psz_unicode, psz_text + i_prev,
2055 sizeof( uint32_t ) * ( k - i_prev ) );
2056 psz_unicode[ k - i_prev ] = 0;
2057 while( *psz_unicode )
2061 if( !(p_line = NewLine( i_len - i_prev)) )
2063 if( p_face ) FT_Done_Face( p_face );
2064 free( pp_char_styles );
2065 free( psz_unicode );
2066 #if defined(HAVE_FRIBIDI)
2069 free( pi_karaoke_bar );
2072 /* New Color mode only works in YUVA rendering mode --
2073 * (RGB mode has palette constraints on it). We therefore
2074 * need to populate the legacy colour fields also.
2076 p_line->b_new_color_mode = true;
2077 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2078 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2079 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2080 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2081 p_line->p_next = NULL;
2083 i_pen_y += tmp_result.y;
2087 if( p_prev ) p_prev->p_next = p_line;
2088 else *pp_lines = p_line;
2091 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2092 p_style->i_font_color, p_style->b_underline,
2093 p_style->i_karaoke_bg_color,
2094 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2095 &tmp_result ) != VLC_SUCCESS )
2097 if( p_face ) FT_Done_Face( p_face );
2098 free( pp_char_styles );
2099 free( psz_unicode );
2100 #if defined(HAVE_FRIBIDI)
2103 free( pi_karaoke_bar );
2104 return VLC_EGENERIC;
2109 p_result->x = __MAX( p_result->x, tmp_result.x );
2110 p_result->y += tmp_result.y;
2115 if( *psz_unicode == '\n')
2119 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2121 *c_ptr = *(c_ptr+1);
2126 free( psz_unicode );
2127 if( p_face ) FT_Done_Face( p_face );
2131 free( pp_char_styles );
2132 #if defined(HAVE_FRIBIDI)
2137 p_result->x = __MAX( p_result->x, tmp_result.x );
2138 p_result->y += tmp_result.y;
2141 if( pi_karaoke_bar )
2144 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2146 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2148 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2152 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2154 /* 100% BG colour will render faster if we
2155 * instead make it 100% FG colour, so leave
2156 * the ratio alone and copy the value across
2158 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2162 if( pi_karaoke_bar[ i ] & 0x80 )
2164 /* Swap Left and Right sides over for Right aligned
2165 * language text (eg. Arabic, Hebrew)
2167 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2169 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2170 p_line->p_bg_rgb[ k ] = i_tmp;
2172 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2175 /* Jump over the '\n' at the line-end */
2178 free( pi_karaoke_bar );
2184 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2185 subpicture_region_t *p_region_in )
2187 int rv = VLC_SUCCESS;
2188 stream_t *p_sub = NULL;
2189 xml_reader_t *p_xml_reader = NULL;
2191 if( !p_region_in || !p_region_in->psz_html )
2192 return VLC_EGENERIC;
2194 /* Reset the default fontsize in case screen metrics have changed */
2195 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2197 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2198 (uint8_t *) p_region_in->psz_html,
2199 strlen( p_region_in->psz_html ),
2203 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2204 if( p_filter->p_sys->p_xml )
2206 bool b_karaoke = false;
2208 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2211 /* Look for Root Node */
2212 if( xml_ReaderRead( p_xml_reader ) == 1 )
2214 char *psz_node = xml_ReaderName( p_xml_reader );
2216 if( !strcasecmp( "karaoke", psz_node ) )
2218 /* We're going to have to render the text a number
2219 * of times to show the progress marker on the text.
2221 var_SetBool( p_filter, "text-rerender", true );
2224 else if( !strcasecmp( "text", psz_node ) )
2230 /* Only text and karaoke tags are supported */
2231 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2232 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2233 p_xml_reader = NULL;
2245 uint32_t i_runs = 0;
2246 uint32_t i_k_runs = 0;
2247 uint32_t *pi_run_lengths = NULL;
2248 uint32_t *pi_k_run_lengths = NULL;
2249 uint32_t *pi_k_durations = NULL;
2250 ft_style_t **pp_styles = NULL;
2252 line_desc_t *p_lines = NULL;
2254 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2255 sizeof( uint32_t ) );
2260 rv = ProcessNodes( p_filter, p_xml_reader,
2261 p_region_in->p_style, psz_text, &i_len,
2262 &i_runs, &pi_run_lengths, &pp_styles,
2264 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2267 p_region_out->i_x = p_region_in->i_x;
2268 p_region_out->i_y = p_region_in->i_y;
2270 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2272 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2273 pi_run_lengths, pp_styles, &p_lines, &result,
2274 b_karaoke, i_k_runs, pi_k_run_lengths,
2278 for( k=0; k<i_runs; k++)
2279 DeleteStyle( pp_styles[k] );
2281 free( pi_run_lengths );
2284 /* Don't attempt to render text that couldn't be layed out
2287 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2289 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2291 Render( p_filter, p_region_out, p_lines,
2292 result.x, result.y );
2296 RenderYUVA( p_filter, p_region_out, p_lines,
2297 result.x, result.y );
2301 FreeLines( p_lines );
2303 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2306 stream_Delete( p_sub );
2312 static char* FontConfig_Select( FcConfig* priv, const char* family,
2313 bool b_bold, bool b_italic, int *i_idx )
2316 FcPattern *pat, *p_pat;
2320 pat = FcPatternCreate();
2321 if (!pat) return NULL;
2323 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2324 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2325 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2326 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2328 FcDefaultSubstitute( pat );
2330 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2332 FcPatternDestroy( pat );
2336 p_pat = FcFontMatch( priv, pat, &result );
2337 FcPatternDestroy( pat );
2338 if( !p_pat ) return NULL;
2340 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2341 || ( val_b != FcTrue ) )
2343 FcPatternDestroy( p_pat );
2346 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2351 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2353 FcPatternDestroy( p_pat );
2358 if( strcasecmp((const char*)val_s, family ) != 0 )
2359 msg_Warn( p_filter, "fontconfig: selected font family is not"
2360 "the requested one: '%s' != '%s'\n",
2361 (const char*)val_s, family );
2364 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2366 FcPatternDestroy( p_pat );
2370 FcPatternDestroy( p_pat );
2371 return strdup( (const char*)val_s );
2375 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2376 uint32_t **psz_text_out, uint32_t *pi_runs,
2377 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2378 ft_style_t *p_style )
2380 VLC_UNUSED(p_filter);
2381 VLC_UNUSED(psz_text_in);
2382 VLC_UNUSED(psz_text_out);
2383 VLC_UNUSED(pi_runs);
2384 VLC_UNUSED(ppi_run_lengths);
2385 VLC_UNUSED(ppp_styles);
2386 VLC_UNUSED(p_style);
2389 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2390 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2394 VLC_UNUSED(p_fonts);
2396 VLC_UNUSED(b_italic);
2397 VLC_UNUSED(b_uline);
2402 static void FreeLine( line_desc_t *p_line )
2405 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2407 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2409 free( p_line->pp_glyphs );
2410 free( p_line->p_glyph_pos );
2411 free( p_line->p_fg_rgb );
2412 free( p_line->p_bg_rgb );
2413 free( p_line->p_fg_bg_ratio );
2414 free( p_line->pi_underline_offset );
2415 free( p_line->pi_underline_thickness );
2419 static void FreeLines( line_desc_t *p_lines )
2421 line_desc_t *p_line, *p_next;
2423 if( !p_lines ) return;
2425 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2427 p_next = p_line->p_next;
2432 static line_desc_t *NewLine( int i_count )
2434 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2436 if( !p_line ) return NULL;
2437 p_line->i_height = 0;
2438 p_line->i_width = 0;
2439 p_line->p_next = NULL;
2441 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2442 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2443 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2444 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2445 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2446 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2447 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2448 if( ( p_line->pp_glyphs == NULL ) ||
2449 ( p_line->p_glyph_pos == NULL ) ||
2450 ( p_line->p_fg_rgb == NULL ) ||
2451 ( p_line->p_bg_rgb == NULL ) ||
2452 ( p_line->p_fg_bg_ratio == NULL ) ||
2453 ( p_line->pi_underline_offset == NULL ) ||
2454 ( p_line->pi_underline_thickness == NULL ) )
2456 free( p_line->pi_underline_thickness );
2457 free( p_line->pi_underline_offset );
2458 free( p_line->p_fg_rgb );
2459 free( p_line->p_bg_rgb );
2460 free( p_line->p_fg_bg_ratio );
2461 free( p_line->p_glyph_pos );
2462 free( p_line->pp_glyphs );
2466 p_line->pp_glyphs[0] = NULL;
2467 p_line->b_new_color_mode = false;
2472 static int GetFontSize( filter_t *p_filter )
2474 filter_sys_t *p_sys = p_filter->p_sys;
2478 if( p_sys->i_default_font_size )
2480 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2481 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2483 i_size = p_sys->i_default_font_size;
2487 var_Get( p_filter, "freetype-rel-fontsize", &val );
2490 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2491 p_filter->p_sys->i_display_height =
2492 p_filter->fmt_out.video.i_height;
2497 msg_Warn( p_filter, "invalid fontsize, using 12" );
2498 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2499 i_size = 12 * val.i_int / 1000;
2506 static int SetFontSize( filter_t *p_filter, int i_size )
2508 filter_sys_t *p_sys = p_filter->p_sys;
2512 i_size = GetFontSize( p_filter );
2514 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2517 p_sys->i_font_size = i_size;
2519 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2521 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2522 return VLC_EGENERIC;
2528 static void YUVFromRGB( uint32_t i_argb,
2529 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2531 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2532 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2533 int i_blue = ( i_argb & 0x000000ff );
2535 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2536 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2537 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2538 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2539 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2540 -585 * i_blue + 4096 + 1048576) >> 13, 240);