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 #import <Carbon/Carbon.h>
58 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
59 #define DEFAULT_FONT_SNOWLEOPARD "/System/Library/Fonts/LucidaGrande.ttc"
60 #define FC_DEFAULT_FONT "Lucida Grande"
61 #elif defined( SYS_BEOS )
62 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
63 #define FC_DEFAULT_FONT "Swiss"
64 #elif defined( WIN32 )
65 #define DEFAULT_FONT "" /* Default font found at run-time */
66 #define FC_DEFAULT_FONT "Arial"
68 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
69 #define FC_DEFAULT_FONT "Serif Bold"
72 #if defined(HAVE_FRIBIDI)
73 #include <fribidi/fribidi.h>
76 #ifdef HAVE_FONTCONFIG
77 #include <fontconfig/fontconfig.h>
79 #define DEFAULT_FONT FC_DEFAULT_FONT
84 /*****************************************************************************
86 *****************************************************************************/
87 static int Create ( vlc_object_t * );
88 static void Destroy( vlc_object_t * );
90 #define FONT_TEXT N_("Font")
92 #ifdef HAVE_FONTCONFIG
93 #define FONT_LONGTEXT N_("Font family for the font you want to use")
95 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
98 #define FONTSIZE_TEXT N_("Font size in pixels")
99 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
100 "that will be rendered on the video. " \
101 "If set to something different than 0 this option will override the " \
102 "relative font size." )
103 #define OPACITY_TEXT N_("Opacity")
104 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
105 "text that will be rendered on the video. 0 = transparent, " \
106 "255 = totally opaque. " )
107 #define COLOR_TEXT N_("Text default color")
108 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
109 "the video. This must be an hexadecimal (like HTML colors). The first two "\
110 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
111 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
112 #define FONTSIZER_TEXT N_("Relative font size")
113 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
114 "fonts that will be rendered on the video. If absolute font size is set, "\
115 "relative size will be overriden." )
117 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
118 static const char *const ppsz_sizes_text[] = {
119 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
120 #define YUVP_TEXT N_("Use YUVP renderer")
121 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
122 "This option is only needed if you want to encode into DVB subtitles" )
123 #define EFFECT_TEXT N_("Font Effect")
124 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
125 "text to improve its readability." )
127 #define EFFECT_BACKGROUND 1
128 #define EFFECT_OUTLINE 2
129 #define EFFECT_OUTLINE_FAT 3
131 static int const pi_effects[] = { 1, 2, 3 };
132 static const char *const ppsz_effects_text[] = {
133 N_("Background"),N_("Outline"), N_("Fat Outline") };
134 static const int pi_color_values[] = {
135 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
136 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
137 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
139 static const char *const ppsz_color_descriptions[] = {
140 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
141 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
142 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
145 set_shortname( N_("Text renderer"))
146 set_description( N_("Freetype2 font renderer") )
147 set_category( CAT_VIDEO )
148 set_subcategory( SUBCAT_VIDEO_SUBPIC )
150 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
153 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
154 FONTSIZE_LONGTEXT, true )
156 /* opacity valid on 0..255, with default 255 = fully opaque */
157 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
158 OPACITY_TEXT, OPACITY_LONGTEXT, true )
160 /* hook to the color values list, with default 0x00ffffff = white */
161 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
162 COLOR_LONGTEXT, false )
163 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
165 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
166 FONTSIZER_LONGTEXT, false )
167 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
168 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
169 EFFECT_LONGTEXT, false )
170 change_integer_list( pi_effects, ppsz_effects_text, NULL )
172 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
173 YUVP_LONGTEXT, true )
174 set_capability( "text renderer", 100 )
175 add_shortcut( "text" )
176 set_callbacks( Create, Destroy )
181 /*****************************************************************************
183 *****************************************************************************/
185 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
186 static int RenderText( filter_t *, subpicture_region_t *,
187 subpicture_region_t * );
188 #ifdef HAVE_FONTCONFIG
189 static int RenderHtml( filter_t *, subpicture_region_t *,
190 subpicture_region_t * );
191 static char *FontConfig_Select( FcConfig *, const char *,
196 static int LoadFontsFromAttachments( filter_t *p_filter );
198 static int GetFontSize( filter_t *p_filter );
199 static int SetFontSize( filter_t *, int );
200 static void YUVFromRGB( uint32_t i_argb,
201 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
203 typedef struct line_desc_t line_desc_t;
206 /** NULL-terminated list of glyphs making the string */
207 FT_BitmapGlyph *pp_glyphs;
208 /** list of relative positions for the glyphs */
209 FT_Vector *p_glyph_pos;
210 /** list of RGB information for styled text
211 * -- if the rendering mode supports it (RenderYUVA) and
212 * b_new_color_mode is set, then it becomes possible to
213 * have multicoloured text within the subtitles. */
216 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
217 bool b_new_color_mode;
218 /** underline information -- only supplied if text should be underlined */
219 uint16_t *pi_underline_offset;
220 uint16_t *pi_underline_thickness;
224 int i_red, i_green, i_blue;
229 static line_desc_t *NewLine( int );
234 uint32_t i_font_color; /* ARGB */
235 uint32_t i_karaoke_bg_color; /* ARGB */
242 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
243 static void FreeLines( line_desc_t * );
244 static void FreeLine( line_desc_t * );
246 /*****************************************************************************
247 * filter_sys_t: freetype local data
248 *****************************************************************************
249 * This structure is part of the video output thread descriptor.
250 * It describes the freetype specific properties of an output thread.
251 *****************************************************************************/
254 FT_Library p_library; /* handle to library */
255 FT_Face p_face; /* handle to face object */
257 uint8_t i_font_opacity;
262 int i_default_font_size;
263 int i_display_height;
264 #ifdef HAVE_FONTCONFIG
265 char* psz_fontfamily;
269 input_attachment_t **pp_font_attachments;
270 int i_font_attachments;
274 #define UCHAR uint32_t
275 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
276 #define TR_FONT_STYLE_PTR ft_style_t *
278 #include "text_renderer.h"
280 /*****************************************************************************
281 * Create: allocates osd-text video thread output method
282 *****************************************************************************
283 * This function allocates and initializes a Clone vout method.
284 *****************************************************************************/
285 static int Create( vlc_object_t *p_this )
287 filter_t *p_filter = (filter_t *)p_this;
289 char *psz_fontfile=NULL;
290 char *psz_fontfamily=NULL;
291 int i_error,fontindex;
293 #ifdef HAVE_FONTCONFIG
294 FcPattern *fontpattern = NULL, *fontmatch = NULL;
295 /* Initialise result to Match, as fontconfig doesnt
296 * really set this other than some error-cases */
297 FcResult fontresult = FcResultMatch;
301 /* Allocate structure */
302 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
305 #ifdef HAVE_FONTCONFIG
306 p_sys->psz_fontfamily = NULL;
310 p_sys->p_library = 0;
311 p_sys->i_font_size = 0;
312 p_sys->i_display_height = 0;
314 var_Create( p_filter, "freetype-rel-fontsize",
315 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
317 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
318 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
319 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
320 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
321 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
322 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
323 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
326 if( !psz_fontfamily || !*psz_fontfamily )
328 #ifdef HAVE_FONTCONFIG
331 free( psz_fontfamily);
332 if (Gestalt(gestaltSystemVersion, &MacVersion) == noErr && MacVersion >= 0x1060)
333 psz_fontfamily=strdup( DEFAULT_FONT_SNOWLEOPARD );
335 psz_fontfamily=strdup( DEFAULT_FONT );
337 free( psz_fontfamily);
338 psz_fontfamily=strdup( DEFAULT_FONT );
341 free( psz_fontfamily );
342 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
343 if( !psz_fontfamily )
346 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
347 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
350 if (Gestalt(gestaltSystemVersion, &MacVersion) == noErr && MacVersion >= 0x1060)
351 strcpy( psz_fontfile, DEFAULT_FONT_SNOWLEOPARD );
353 strcpy( psz_fontfile, DEFAULT_FONT );
355 strcpy( psz_fontfamily, DEFAULT_FONT );
357 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
361 #ifdef HAVE_FONTCONFIG
362 /* Lets find some fontfile from freetype-font variable family */
364 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
368 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
369 _("Building font cache"),
370 _("Please wait while your font cache is rebuilt.\n"
371 "This should take less than few minutes."), NULL );
373 path = (char *)malloc( PATH_MAX + 1 );
374 /* Fontconfig doesnt seem to know where windows fonts are with
375 * current contribs. So just tell default windows font directory
376 * is the place to search fonts
378 GetWindowsDirectory( path, PATH_MAX + 1 );
379 strcat( path, "\\fonts" );
381 dialog_ProgressSet( p_dialog, NULL, 0.4 );
383 FcConfigAppFontAddDir( NULL , path );
388 dialog_ProgressSet( p_dialog, NULL, 0.5 );
392 msg_Dbg( p_filter, "Building font database.");
394 FcConfigBuildFonts( NULL );
397 msg_Dbg( p_filter, "Finished building font database." );
398 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
400 fontpattern = FcPatternCreate();
404 msg_Err( p_filter, "Creating fontpattern failed");
410 dialog_ProgressSet( p_dialog, NULL, 0.7 );
412 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
413 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
414 free( psz_fontsize );
416 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
418 msg_Err( p_filter, "FontSubstitute failed");
421 FcDefaultSubstitute( fontpattern );
425 dialog_ProgressSet( p_dialog, NULL, 0.8 );
427 /* testing fontresult here doesn't do any good really, but maybe it will
428 * in future as fontconfig code doesn't set it in all cases and just
429 * returns NULL or doesn't set to to Match on all Match cases.*/
430 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
431 if( !fontmatch || fontresult == FcResultNoMatch )
433 msg_Err( p_filter, "Fontmatching failed");
437 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
438 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
441 msg_Err( p_filter, "Failed to get fontfile");
445 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
446 p_sys->psz_fontfamily = strdup( psz_fontfamily );
450 dialog_ProgressSet( p_dialog, NULL, 1.0 );
451 dialog_ProgressDestroy( p_dialog );
457 #ifdef HAVE_FONTCONFIG
458 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
459 psz_fontfile = psz_fontfamily;
464 i_error = FT_Init_FreeType( &p_sys->p_library );
467 msg_Err( p_filter, "couldn't initialize freetype" );
471 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
472 fontindex, &p_sys->p_face );
474 if( i_error == FT_Err_Unknown_File_Format )
476 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
481 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
485 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
488 msg_Err( p_filter, "font has no unicode translation table" );
492 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
494 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
497 p_sys->pp_font_attachments = NULL;
498 p_sys->i_font_attachments = 0;
500 p_filter->pf_render_text = RenderText;
501 #ifdef HAVE_FONTCONFIG
502 p_filter->pf_render_html = RenderHtml;
503 FcPatternDestroy( fontmatch );
504 FcPatternDestroy( fontpattern );
506 p_filter->pf_render_html = NULL;
509 free( psz_fontfamily );
510 LoadFontsFromAttachments( p_filter );
515 #ifdef HAVE_FONTCONFIG
516 if( fontmatch ) FcPatternDestroy( fontmatch );
517 if( fontpattern ) FcPatternDestroy( fontpattern );
519 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
520 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
521 free( psz_fontfamily );
526 /*****************************************************************************
527 * Destroy: destroy Clone video thread output method
528 *****************************************************************************
529 * Clean up all data and library connections
530 *****************************************************************************/
531 static void Destroy( vlc_object_t *p_this )
533 filter_t *p_filter = (filter_t *)p_this;
534 filter_sys_t *p_sys = p_filter->p_sys;
536 if( p_sys->pp_font_attachments )
540 for( k = 0; k < p_sys->i_font_attachments; k++ )
541 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
543 free( p_sys->pp_font_attachments );
546 #ifdef HAVE_FONTCONFIG
547 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
548 free( p_sys->psz_fontfamily );
551 /* FcFini asserts calling the subfunction FcCacheFini()
552 * even if no other library functions have been made since FcInit(),
553 * so don't call it. */
555 FT_Done_Face( p_sys->p_face );
556 FT_Done_FreeType( p_sys->p_library );
560 /*****************************************************************************
561 * Make any TTF/OTF fonts present in the attachments of the media file
562 * and store them for later use by the FreeType Engine
563 *****************************************************************************/
564 static int LoadFontsFromAttachments( filter_t *p_filter )
566 filter_sys_t *p_sys = p_filter->p_sys;
567 input_thread_t *p_input;
568 input_attachment_t **pp_attachments;
569 int i_attachments_cnt;
571 int rv = VLC_SUCCESS;
573 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
577 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
579 vlc_object_release(p_input);
583 p_sys->i_font_attachments = 0;
584 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
585 if(! p_sys->pp_font_attachments )
588 for( k = 0; k < i_attachments_cnt; k++ )
590 input_attachment_t *p_attach = pp_attachments[k];
592 if( p_sys->pp_font_attachments )
594 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
595 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
596 ( p_attach->i_data > 0 ) &&
597 ( p_attach->p_data != NULL ) )
599 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
603 vlc_input_attachment_Delete( p_attach );
608 vlc_input_attachment_Delete( p_attach );
611 free( pp_attachments );
613 vlc_object_release(p_input);
618 /*****************************************************************************
619 * Render: place string in picture
620 *****************************************************************************
621 * This function merges the previously rendered freetype glyphs into a picture
622 *****************************************************************************/
623 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
624 line_desc_t *p_line, int i_width, int i_height )
626 VLC_UNUSED(p_filter);
627 static const uint8_t pi_gamma[16] =
628 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
629 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
633 int i, x, y, i_pitch;
634 uint8_t i_y; /* YUV values, derived from incoming RGB */
637 /* Create a new subpicture region */
638 memset( &fmt, 0, sizeof(video_format_t) );
639 fmt.i_chroma = VLC_CODEC_YUVP;
641 fmt.i_width = fmt.i_visible_width = i_width + 4;
642 fmt.i_height = fmt.i_visible_height = i_height + 4;
643 if( p_region->fmt.i_visible_width > 0 )
644 fmt.i_visible_width = p_region->fmt.i_visible_width;
645 if( p_region->fmt.i_visible_height > 0 )
646 fmt.i_visible_height = p_region->fmt.i_visible_height;
647 fmt.i_x_offset = fmt.i_y_offset = 0;
649 assert( !p_region->p_picture );
650 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
651 if( !p_region->p_picture )
655 /* Calculate text color components */
656 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
657 25 * p_line->i_blue + 128) >> 8) + 16;
658 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
659 112 * p_line->i_blue + 128) >> 8) + 128;
660 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
661 18 * p_line->i_blue + 128) >> 8) + 128;
664 fmt.p_palette->i_entries = 16;
665 for( i = 0; i < 8; i++ )
667 fmt.p_palette->palette[i][0] = 0;
668 fmt.p_palette->palette[i][1] = 0x80;
669 fmt.p_palette->palette[i][2] = 0x80;
670 fmt.p_palette->palette[i][3] = pi_gamma[i];
671 fmt.p_palette->palette[i][3] =
672 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
674 for( i = 8; i < fmt.p_palette->i_entries; i++ )
676 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
677 fmt.p_palette->palette[i][1] = i_u;
678 fmt.p_palette->palette[i][2] = i_v;
679 fmt.p_palette->palette[i][3] = pi_gamma[i];
680 fmt.p_palette->palette[i][3] =
681 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
684 p_dst = p_region->p_picture->Y_PIXELS;
685 i_pitch = p_region->p_picture->Y_PITCH;
687 /* Initialize the region pixels */
688 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
690 for( ; p_line != NULL; p_line = p_line->p_next )
692 int i_glyph_tmax = 0;
693 int i_bitmap_offset, i_offset, i_align_offset = 0;
694 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
696 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
697 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
700 if( p_line->i_width < i_width )
702 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
704 i_align_offset = i_width - p_line->i_width;
706 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
708 i_align_offset = ( i_width - p_line->i_width ) / 2;
712 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
714 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
716 i_offset = ( p_line->p_glyph_pos[ i ].y +
717 i_glyph_tmax - p_glyph->top + 2 ) *
718 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
721 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
723 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
725 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
727 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
734 /* Outlining (find something better than nearest neighbour filtering ?) */
737 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
738 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
739 uint8_t left, current;
741 for( y = 1; y < (int)fmt.i_height - 1; y++ )
743 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
744 p_dst += p_region->p_picture->Y_PITCH;
747 for( x = 1; x < (int)fmt.i_width - 1; x++ )
750 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
751 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;
755 memset( p_top, 0, fmt.i_width );
761 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
762 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
763 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
764 int i_glyph_tmax, int i_align_offset,
765 uint8_t i_y, uint8_t i_u, uint8_t i_v,
766 subpicture_region_t *p_region)
770 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
772 p_dst_y = p_region->p_picture->Y_PIXELS;
773 p_dst_u = p_region->p_picture->U_PIXELS;
774 p_dst_v = p_region->p_picture->V_PIXELS;
775 p_dst_a = p_region->p_picture->A_PIXELS;
776 i_pitch = p_region->p_picture->A_PITCH;
778 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
779 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
781 for( y = 0; y < i_line_thickness; y++ )
783 int i_extra = p_this_glyph->bitmap.width;
787 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
788 (p_this_glyph_pos->x + p_this_glyph->left);
790 for( x = 0; x < i_extra; x++ )
794 /* break the underline around the tails of any glyphs which cross it */
795 for( z = x - i_line_thickness;
796 z < x + i_line_thickness && b_ok;
799 if( p_next_glyph && ( z >= i_extra ) )
801 int i_row = i_line_offset + p_next_glyph->top + y;
803 if( ( p_next_glyph->bitmap.rows > i_row ) &&
804 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
809 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
811 int i_row = i_line_offset + p_this_glyph->top + y;
813 if( ( p_this_glyph->bitmap.rows > i_row ) &&
814 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
823 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
824 p_dst_u[i_offset+x] = i_u;
825 p_dst_v[i_offset+x] = i_v;
826 p_dst_a[i_offset+x] = 255;
833 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
835 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
836 int i_pitch = p_region->p_picture->A_PITCH;
839 for( ; p_line != NULL; p_line = p_line->p_next )
841 int i_glyph_tmax=0, i = 0;
842 int i_bitmap_offset, i_offset, i_align_offset = 0;
843 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
845 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
846 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
849 if( p_line->i_width < i_width )
851 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
853 i_align_offset = i_width - p_line->i_width;
855 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
857 i_align_offset = ( i_width - p_line->i_width ) / 2;
861 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
863 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
865 i_offset = ( p_line->p_glyph_pos[ i ].y +
866 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
867 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
868 i_align_offset +xoffset;
870 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
872 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
874 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
875 if( p_dst[i_offset+x] <
876 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
878 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
887 /*****************************************************************************
888 * Render: place string in picture
889 *****************************************************************************
890 * This function merges the previously rendered freetype glyphs into a picture
891 *****************************************************************************/
892 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
893 line_desc_t *p_line, int i_width, int i_height )
895 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
897 int i, x, y, i_pitch, i_alpha;
898 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
900 if( i_width == 0 || i_height == 0 )
903 /* Create a new subpicture region */
904 memset( &fmt, 0, sizeof(video_format_t) );
905 fmt.i_chroma = VLC_CODEC_YUVA;
907 fmt.i_width = fmt.i_visible_width = i_width + 6;
908 fmt.i_height = fmt.i_visible_height = i_height + 6;
909 if( p_region->fmt.i_visible_width > 0 )
910 fmt.i_visible_width = p_region->fmt.i_visible_width;
911 if( p_region->fmt.i_visible_height > 0 )
912 fmt.i_visible_height = p_region->fmt.i_visible_height;
913 fmt.i_x_offset = fmt.i_y_offset = 0;
915 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
916 if( !p_region->p_picture )
920 /* Calculate text color components */
921 YUVFromRGB( (p_line->i_red << 16) |
922 (p_line->i_green << 8) |
925 i_alpha = p_line->i_alpha;
927 p_dst_y = p_region->p_picture->Y_PIXELS;
928 p_dst_u = p_region->p_picture->U_PIXELS;
929 p_dst_v = p_region->p_picture->V_PIXELS;
930 p_dst_a = p_region->p_picture->A_PIXELS;
931 i_pitch = p_region->p_picture->A_PITCH;
933 /* Initialize the region pixels */
934 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
936 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
937 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
938 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
939 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
943 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
944 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
945 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
946 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
948 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
949 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
951 DrawBlack( p_line, i_width, p_region, 0, 0);
952 DrawBlack( p_line, i_width, p_region, -1, 0);
953 DrawBlack( p_line, i_width, p_region, 0, -1);
954 DrawBlack( p_line, i_width, p_region, 1, 0);
955 DrawBlack( p_line, i_width, p_region, 0, 1);
958 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
960 DrawBlack( p_line, i_width, p_region, -1, -1);
961 DrawBlack( p_line, i_width, p_region, -1, 1);
962 DrawBlack( p_line, i_width, p_region, 1, -1);
963 DrawBlack( p_line, i_width, p_region, 1, 1);
965 DrawBlack( p_line, i_width, p_region, -2, 0);
966 DrawBlack( p_line, i_width, p_region, 0, -2);
967 DrawBlack( p_line, i_width, p_region, 2, 0);
968 DrawBlack( p_line, i_width, p_region, 0, 2);
970 DrawBlack( p_line, i_width, p_region, -2, -2);
971 DrawBlack( p_line, i_width, p_region, -2, 2);
972 DrawBlack( p_line, i_width, p_region, 2, -2);
973 DrawBlack( p_line, i_width, p_region, 2, 2);
975 DrawBlack( p_line, i_width, p_region, -3, 0);
976 DrawBlack( p_line, i_width, p_region, 0, -3);
977 DrawBlack( p_line, i_width, p_region, 3, 0);
978 DrawBlack( p_line, i_width, p_region, 0, 3);
981 for( ; p_line != NULL; p_line = p_line->p_next )
983 int i_glyph_tmax = 0;
984 int i_bitmap_offset, i_offset, i_align_offset = 0;
985 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
987 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
988 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
991 if( p_line->i_width < i_width )
993 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
995 i_align_offset = i_width - p_line->i_width;
997 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
999 i_align_offset = ( i_width - p_line->i_width ) / 2;
1003 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1005 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1007 i_offset = ( p_line->p_glyph_pos[ i ].y +
1008 i_glyph_tmax - p_glyph->top + 3 ) *
1009 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1012 if( p_line->b_new_color_mode )
1014 /* Every glyph can (and in fact must) have its own color */
1015 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1018 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1020 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1022 uint8_t i_y_local = i_y;
1023 uint8_t i_u_local = i_u;
1024 uint8_t i_v_local = i_v;
1026 if( p_line->p_fg_bg_ratio != 0x00 )
1028 int i_split = p_glyph->bitmap.width *
1029 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1033 YUVFromRGB( p_line->p_bg_rgb[ i ],
1034 &i_y_local, &i_u_local, &i_v_local );
1038 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1040 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1041 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1043 p_dst_u[i_offset+x] = i_u;
1044 p_dst_v[i_offset+x] = i_v;
1046 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1047 p_dst_a[i_offset+x] = 0xff;
1050 i_offset += i_pitch;
1053 if( p_line->pi_underline_thickness[ i ] )
1055 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1056 p_line->pi_underline_offset[ i ],
1057 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1058 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1059 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1060 i_glyph_tmax, i_align_offset,
1067 /* Apply the alpha setting */
1068 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1069 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1075 * This function renders a text subpicture region into another one.
1076 * It also calculates the size needed for this string, and renders the
1077 * needed glyphs into memory. It is used as pf_add_string callback in
1078 * the vout method by this module
1080 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1081 subpicture_region_t *p_region_in )
1083 filter_sys_t *p_sys = p_filter->p_sys;
1084 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1085 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1086 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1087 int i_string_length;
1089 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1090 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1100 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1101 psz_string = p_region_in->psz_text;
1102 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1104 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1105 i_scale = val.i_int;
1107 if( p_region_in->p_style )
1109 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1110 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1111 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1115 i_font_color = p_sys->i_font_color;
1116 i_font_alpha = 255 - p_sys->i_font_opacity;
1117 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1120 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1121 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1122 SetFontSize( p_filter, i_font_size );
1124 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1125 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1126 i_blue = i_font_color & 0x000000FF;
1128 result.x = result.y = 0;
1129 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1131 psz_unicode = psz_unicode_orig =
1132 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1133 if( psz_unicode == NULL )
1135 #if defined(WORDS_BIGENDIAN)
1136 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1138 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1140 if( iconv_handle == (vlc_iconv_t)-1 )
1142 msg_Warn( p_filter, "unable to do conversion" );
1148 const char *p_in_buffer = psz_string;
1149 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1150 i_in_bytes = strlen( psz_string );
1151 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1152 i_out_bytes_left = i_out_bytes;
1153 p_out_buffer = (char *)psz_unicode;
1154 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1156 &p_out_buffer, &i_out_bytes_left );
1158 vlc_iconv_close( iconv_handle );
1162 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1163 "bytes left %u", (unsigned)i_in_bytes );
1166 *(uint32_t*)p_out_buffer = 0;
1167 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1170 #if defined(HAVE_FRIBIDI)
1172 uint32_t *p_fribidi_string;
1173 int32_t start_pos, pos = 0;
1175 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1176 if( !p_fribidi_string )
1179 /* Do bidi conversion line-by-line */
1180 while( pos < i_string_length )
1182 while( pos < i_string_length )
1184 i_char = psz_unicode[pos];
1185 if (i_char != '\r' && i_char != '\n')
1187 p_fribidi_string[pos] = i_char;
1191 while( pos < i_string_length )
1193 i_char = psz_unicode[pos];
1194 if (i_char == '\r' || i_char == '\n')
1198 if (pos > start_pos)
1200 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1201 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1204 (FriBidiChar*)p_fribidi_string + start_pos,
1209 free( psz_unicode_orig );
1210 psz_unicode = psz_unicode_orig = p_fribidi_string;
1211 p_fribidi_string[ i_string_length ] = 0;
1215 /* Calculate relative glyph positions and a bounding box for the
1217 if( !(p_line = NewLine( strlen( psz_string ))) )
1220 i_pen_x = i_pen_y = 0;
1222 psz_line_start = psz_unicode;
1224 #define face p_sys->p_face
1225 #define glyph face->glyph
1227 while( *psz_unicode )
1229 i_char = *psz_unicode++;
1230 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1235 if( i_char == '\n' )
1237 psz_line_start = psz_unicode;
1238 if( !(p_next = NewLine( strlen( psz_string ))) )
1240 p_line->p_next = p_next;
1241 p_line->i_width = line.xMax;
1242 p_line->i_height = face->size->metrics.height >> 6;
1243 p_line->pp_glyphs[ i ] = NULL;
1244 p_line->i_alpha = i_font_alpha;
1245 p_line->i_red = i_red;
1246 p_line->i_green = i_green;
1247 p_line->i_blue = i_blue;
1250 result.x = __MAX( result.x, line.xMax );
1251 result.y += face->size->metrics.height >> 6;
1254 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1255 i_pen_y += face->size->metrics.height >> 6;
1257 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1262 i_glyph_index = FT_Get_Char_Index( face, i_char );
1263 if( p_sys->i_use_kerning && i_glyph_index
1267 FT_Get_Kerning( face, i_previous, i_glyph_index,
1268 ft_kerning_default, &delta );
1269 i_pen_x += delta.x >> 6;
1272 p_line->p_glyph_pos[ i ].x = i_pen_x;
1273 p_line->p_glyph_pos[ i ].y = i_pen_y;
1274 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1277 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1281 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1284 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1288 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1289 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1292 FT_Done_Glyph( tmp_glyph );
1295 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1298 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1299 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1300 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1302 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1303 p_line->pp_glyphs[ i ] = NULL;
1305 p_line = NewLine( strlen( psz_string ));
1306 if( p_prev ) p_prev->p_next = p_line;
1307 else p_lines = p_line;
1309 uint32_t *psz_unicode_saved = psz_unicode;
1310 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1314 if( psz_unicode == psz_line_start )
1315 { /* try harder to break that line */
1316 psz_unicode = psz_unicode_saved;
1317 while( psz_unicode > psz_line_start &&
1318 *psz_unicode != '_' && *psz_unicode != '/' &&
1319 *psz_unicode != '\\' && *psz_unicode != '.' )
1324 if( psz_unicode == psz_line_start )
1326 msg_Warn( p_filter, "unbreakable string" );
1331 *psz_unicode = '\n';
1333 psz_unicode = psz_line_start;
1336 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1339 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1340 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1342 i_previous = i_glyph_index;
1343 i_pen_x += glyph->advance.x >> 6;
1347 p_line->i_width = line.xMax;
1348 p_line->i_height = face->size->metrics.height >> 6;
1349 p_line->pp_glyphs[ i ] = NULL;
1350 p_line->i_alpha = i_font_alpha;
1351 p_line->i_red = i_red;
1352 p_line->i_green = i_green;
1353 p_line->i_blue = i_blue;
1354 result.x = __MAX( result.x, line.xMax );
1355 result.y += line.yMax - line.yMin;
1360 p_region_out->i_x = p_region_in->i_x;
1361 p_region_out->i_y = p_region_in->i_y;
1363 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1364 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1366 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1368 free( psz_unicode_orig );
1369 FreeLines( p_lines );
1373 free( psz_unicode_orig );
1374 FreeLines( p_lines );
1375 return VLC_EGENERIC;
1378 #ifdef HAVE_FONTCONFIG
1379 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1380 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1381 bool b_italic, bool b_uline )
1383 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1387 p_style->i_font_size = i_font_size;
1388 p_style->i_font_color = i_font_color;
1389 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1390 p_style->b_italic = b_italic;
1391 p_style->b_bold = b_bold;
1392 p_style->b_underline = b_uline;
1394 p_style->psz_fontname = strdup( psz_fontname );
1399 static void DeleteStyle( ft_style_t *p_style )
1403 free( p_style->psz_fontname );
1408 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1415 if(( s1->i_font_size == s2->i_font_size ) &&
1416 ( s1->i_font_color == s2->i_font_color ) &&
1417 ( s1->b_italic == s2->b_italic ) &&
1418 ( s1->b_bold == s2->b_bold ) &&
1419 ( s1->b_underline == s2->b_underline ) &&
1420 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1427 static void IconvText( filter_t *p_filter, const char *psz_string,
1428 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1430 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1432 /* If memory hasn't been allocated for our output string, allocate it here
1433 * - the calling function must now be responsible for freeing it.
1435 if( !*ppsz_unicode )
1436 *ppsz_unicode = (uint32_t *)
1437 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1439 /* We don't need to handle a NULL pointer in *ppsz_unicode
1440 * if we are instead testing for a non NULL value like we are here */
1444 #if defined(WORDS_BIGENDIAN)
1445 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1447 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1449 if( iconv_handle != (vlc_iconv_t)-1 )
1451 char *p_in_buffer, *p_out_buffer;
1452 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1453 i_in_bytes = strlen( psz_string );
1454 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1455 i_out_bytes_left = i_out_bytes;
1456 p_in_buffer = (char *) psz_string;
1457 p_out_buffer = (char *) *ppsz_unicode;
1458 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1459 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1461 vlc_iconv_close( iconv_handle );
1465 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1466 "bytes left %u", (unsigned)i_in_bytes );
1470 *(uint32_t*)p_out_buffer = 0;
1472 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1477 msg_Warn( p_filter, "unable to do conversion" );
1482 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1483 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1486 ft_style_t *p_style = NULL;
1488 char *psz_fontname = NULL;
1489 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1490 uint32_t i_karaoke_bg_color = i_font_color;
1491 int i_font_size = p_sys->i_font_size;
1493 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1494 &i_font_color, &i_karaoke_bg_color ))
1496 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1497 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1502 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1503 bool b_uline, int i_karaoke_bgcolor,
1504 line_desc_t *p_line, uint32_t *psz_unicode,
1505 int *pi_pen_x, int i_pen_y, int *pi_start,
1506 FT_Vector *p_result )
1511 bool b_first_on_line = true;
1514 int i_pen_x_start = *pi_pen_x;
1516 uint32_t *psz_unicode_start = psz_unicode;
1518 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1520 /* Account for part of line already in position */
1521 for( i=0; i<*pi_start; i++ )
1525 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1526 ft_glyph_bbox_pixels, &glyph_size );
1528 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1529 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1530 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1531 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1537 b_first_on_line = false;
1539 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1545 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1546 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1550 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1551 ft_kerning_default, &delta );
1552 *pi_pen_x += delta.x >> 6;
1554 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1555 p_line->p_glyph_pos[ i ].y = i_pen_y;
1557 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1561 "unable to render text FT_Load_Glyph returned %d", i_error );
1562 p_line->pp_glyphs[ i ] = NULL;
1563 return VLC_EGENERIC;
1565 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1569 "unable to render text FT_Get_Glyph returned %d", i_error );
1570 p_line->pp_glyphs[ i ] = NULL;
1571 return VLC_EGENERIC;
1573 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1574 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1577 FT_Done_Glyph( tmp_glyph );
1582 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1583 p_face->size->metrics.y_scale));
1584 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1585 p_face->size->metrics.y_scale));
1587 p_line->pi_underline_offset[ i ] =
1588 ( aOffset < 0 ) ? -aOffset : aOffset;
1589 p_line->pi_underline_thickness[ i ] =
1590 ( aSize < 0 ) ? -aSize : aSize;
1592 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1593 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1594 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1595 p_line->p_fg_bg_ratio[ i ] = 0x00;
1597 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1598 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1599 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1601 for( ; i >= *pi_start; i-- )
1602 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1605 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1609 if( psz_unicode == psz_unicode_start )
1611 if( b_first_on_line )
1613 msg_Warn( p_filter, "unbreakable string" );
1614 p_line->pp_glyphs[ i ] = NULL;
1615 return VLC_EGENERIC;
1617 *pi_pen_x = i_pen_x_start;
1619 p_line->i_width = line.xMax;
1620 p_line->i_height = __MAX( p_line->i_height,
1621 p_face->size->metrics.height >> 6 );
1622 p_line->pp_glyphs[ i ] = NULL;
1624 p_result->x = __MAX( p_result->x, line.xMax );
1625 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1626 i_yMax - i_yMin ) );
1631 *psz_unicode = '\n';
1633 psz_unicode = psz_unicode_start;
1634 *pi_pen_x = i_pen_x_start;
1642 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1643 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1645 i_previous = i_glyph_index;
1646 *pi_pen_x += p_face->glyph->advance.x >> 6;
1649 p_line->i_width = line.xMax;
1650 p_line->i_height = __MAX( p_line->i_height,
1651 p_face->size->metrics.height >> 6 );
1652 p_line->pp_glyphs[ i ] = NULL;
1654 p_result->x = __MAX( p_result->x, line.xMax );
1655 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1656 line.yMax - line.yMin ) );
1660 /* Get rid of any text processed - if necessary repositioning
1661 * at the start of a new line of text
1665 *psz_unicode_start = '\0';
1667 else if( psz_unicode > psz_unicode_start )
1669 for( i=0; psz_unicode[ i ]; i++ )
1670 psz_unicode_start[ i ] = psz_unicode[ i ];
1671 psz_unicode_start[ i ] = '\0';
1677 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1678 uint32_t **psz_text_out, uint32_t *pi_runs,
1679 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1680 ft_style_t *p_style )
1682 uint32_t i_string_length = 0;
1684 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1685 *psz_text_out += i_string_length;
1687 if( ppp_styles && ppi_run_lengths )
1693 *ppp_styles = (ft_style_t **)
1694 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1696 else if( *pi_runs == 1 )
1698 *ppp_styles = (ft_style_t **)
1699 malloc( *pi_runs * sizeof( ft_style_t * ) );
1702 /* We have just malloc'ed this memory successfully -
1703 * *pi_runs HAS to be within the memory area of *ppp_styles */
1706 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1710 if( *ppi_run_lengths )
1712 *ppi_run_lengths = (uint32_t *)
1713 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1715 else if( *pi_runs == 1 )
1717 *ppi_run_lengths = (uint32_t *)
1718 malloc( *pi_runs * sizeof( uint32_t ) );
1721 /* same remarks here */
1722 if( *ppi_run_lengths )
1724 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1727 /* If we couldn't use the p_style argument due to memory allocation
1728 * problems above, release it here.
1730 if( p_style ) DeleteStyle( p_style );
1733 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1737 for( k=0; k < p_sys->i_font_attachments; k++ )
1739 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1741 FT_Face p_face = NULL;
1743 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1751 bool match = !strcasecmp( p_face->family_name,
1752 p_style->psz_fontname );
1754 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1755 match = match && p_style->b_bold;
1757 match = match && !p_style->b_bold;
1759 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1760 match = match && p_style->b_italic;
1762 match = match && !p_style->b_italic;
1770 FT_Done_Face( p_face );
1775 return VLC_EGENERIC;
1778 static int ProcessLines( filter_t *p_filter,
1783 uint32_t *pi_run_lengths,
1784 ft_style_t **pp_styles,
1785 line_desc_t **pp_lines,
1787 FT_Vector *p_result,
1791 uint32_t *pi_k_run_lengths,
1792 uint32_t *pi_k_durations )
1794 filter_sys_t *p_sys = p_filter->p_sys;
1795 ft_style_t **pp_char_styles;
1796 int *p_new_positions = NULL;
1797 int8_t *p_levels = NULL;
1798 uint8_t *pi_karaoke_bar = NULL;
1802 /* Assign each character in the text string its style explicitly, so that
1803 * after the characters have been shuffled around by Fribidi, we can re-apply
1804 * the styles, and to simplify the calculation of runs within a line.
1806 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1807 if( !pp_char_styles )
1812 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1813 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1814 * we just won't be able to display the progress bar; at least we'll
1820 for( j = 0; j < i_runs; j++ )
1821 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1822 pp_char_styles[ i++ ] = pp_styles[ j ];
1824 #if defined(HAVE_FRIBIDI)
1826 ft_style_t **pp_char_styles_new;
1827 int *p_old_positions;
1828 uint32_t *p_fribidi_string;
1829 int start_pos, pos = 0;
1831 pp_char_styles_new = (ft_style_t **)
1832 malloc( i_len * sizeof( ft_style_t * ));
1834 p_fribidi_string = (uint32_t *)
1835 malloc( (i_len + 1) * sizeof(uint32_t) );
1836 p_old_positions = (int *)
1837 malloc( (i_len + 1) * sizeof( int ) );
1838 p_new_positions = (int *)
1839 malloc( (i_len + 1) * sizeof( int ) );
1840 p_levels = (int8_t *)
1841 malloc( (i_len + 1) * sizeof( int8_t ) );
1843 if( ! pp_char_styles_new ||
1844 ! p_fribidi_string ||
1845 ! p_old_positions ||
1846 ! p_new_positions ||
1850 free( p_old_positions );
1851 free( p_new_positions );
1852 free( p_fribidi_string );
1853 free( pp_char_styles_new );
1854 free( pi_karaoke_bar );
1856 free( pp_char_styles );
1860 /* Do bidi conversion line-by-line */
1863 while(pos < i_len) {
1864 if (psz_text[pos] != '\n')
1866 p_fribidi_string[pos] = psz_text[pos];
1867 pp_char_styles_new[pos] = pp_char_styles[pos];
1868 p_new_positions[pos] = pos;
1873 while(pos < i_len) {
1874 if (psz_text[pos] == '\n')
1878 if (pos > start_pos)
1880 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1881 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1882 pos - start_pos, &base_dir,
1883 (FriBidiChar*)p_fribidi_string + start_pos,
1884 p_new_positions + start_pos,
1886 p_levels + start_pos );
1887 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1889 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1890 p_old_positions[ j - start_pos ] ];
1891 p_new_positions[ j ] += start_pos;
1895 free( p_old_positions );
1896 free( pp_char_styles );
1897 pp_char_styles = pp_char_styles_new;
1898 psz_text = p_fribidi_string;
1899 p_fribidi_string[ i_len ] = 0;
1902 /* Work out the karaoke */
1903 if( pi_karaoke_bar )
1905 int64_t i_last_duration = 0;
1906 int64_t i_duration = 0;
1907 int64_t i_start_pos = 0;
1908 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1910 for( k = 0; k< i_k_runs; k++ )
1912 double fraction = 0.0;
1914 i_duration += pi_k_durations[ k ];
1916 if( i_duration < i_elapsed )
1918 /* Completely finished this run-length -
1919 * let it render normally */
1923 else if( i_elapsed < i_last_duration )
1925 /* Haven't got up to this segment yet -
1926 * render it completely in karaoke BG mode */
1932 /* Partway through this run */
1934 fraction = (double)(i_elapsed - i_last_duration) /
1935 (double)pi_k_durations[ k ];
1937 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1939 double shade = pi_k_run_lengths[ k ] * fraction;
1941 if( p_new_positions )
1942 j = p_new_positions[ i_start_pos + i ];
1944 j = i_start_pos + i;
1946 if( i < (uint32_t)shade )
1947 pi_karaoke_bar[ j ] = 0xff;
1948 else if( (double)i > shade )
1949 pi_karaoke_bar[ j ] = 0x00;
1952 shade -= (int)shade;
1953 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1954 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1958 i_last_duration = i_duration;
1959 i_start_pos += pi_k_run_lengths[ k ];
1963 free( p_new_positions );
1965 FT_Vector tmp_result;
1967 line_desc_t *p_line = NULL;
1968 line_desc_t *p_prev = NULL;
1974 p_result->x = p_result->y = 0;
1975 tmp_result.x = tmp_result.y = 0;
1978 for( k = 0; k <= (uint32_t) i_len; k++ )
1980 if( ( k == (uint32_t) i_len ) ||
1982 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1984 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1986 /* End of the current style run */
1987 FT_Face p_face = NULL;
1990 /* Look for a match amongst our attachments first */
1991 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1995 char *psz_fontfile = NULL;
1997 psz_fontfile = FontConfig_Select( NULL,
1998 p_style->psz_fontname,
2002 if( psz_fontfile && ! *psz_fontfile )
2004 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2005 " so using default font", p_style->psz_fontname,
2006 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2007 (p_style->b_bold ? "(Bold)" :
2008 (p_style->b_italic ? "(Italic)" : ""))) );
2009 free( psz_fontfile );
2010 psz_fontfile = NULL;
2015 if( FT_New_Face( p_sys->p_library,
2016 psz_fontfile, i_idx, &p_face ) )
2018 free( psz_fontfile );
2019 free( pp_char_styles );
2020 #if defined(HAVE_FRIBIDI)
2023 free( pi_karaoke_bar );
2024 return VLC_EGENERIC;
2026 free( psz_fontfile );
2030 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2032 /* We've loaded a font face which is unhelpful for actually
2033 * rendering text - fallback to the default one.
2035 FT_Done_Face( p_face );
2039 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2040 ft_encoding_unicode ) ||
2041 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2042 p_style->i_font_size ) )
2044 if( p_face ) FT_Done_Face( p_face );
2045 free( pp_char_styles );
2046 #if defined(HAVE_FRIBIDI)
2049 free( pi_karaoke_bar );
2050 return VLC_EGENERIC;
2052 p_sys->i_use_kerning =
2053 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2056 uint32_t *psz_unicode = (uint32_t *)
2057 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2060 if( p_face ) FT_Done_Face( p_face );
2061 free( pp_char_styles );
2062 free( psz_unicode );
2063 #if defined(HAVE_FRIBIDI)
2066 free( pi_karaoke_bar );
2069 memcpy( psz_unicode, psz_text + i_prev,
2070 sizeof( uint32_t ) * ( k - i_prev ) );
2071 psz_unicode[ k - i_prev ] = 0;
2072 while( *psz_unicode )
2076 if( !(p_line = NewLine( i_len - i_prev)) )
2078 if( p_face ) FT_Done_Face( p_face );
2079 free( pp_char_styles );
2080 free( psz_unicode );
2081 #if defined(HAVE_FRIBIDI)
2084 free( pi_karaoke_bar );
2087 /* New Color mode only works in YUVA rendering mode --
2088 * (RGB mode has palette constraints on it). We therefore
2089 * need to populate the legacy colour fields also.
2091 p_line->b_new_color_mode = true;
2092 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2093 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2094 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2095 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2096 p_line->p_next = NULL;
2098 i_pen_y += tmp_result.y;
2102 if( p_prev ) p_prev->p_next = p_line;
2103 else *pp_lines = p_line;
2106 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2107 p_style->i_font_color, p_style->b_underline,
2108 p_style->i_karaoke_bg_color,
2109 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2110 &tmp_result ) != VLC_SUCCESS )
2112 if( p_face ) FT_Done_Face( p_face );
2113 free( pp_char_styles );
2114 free( psz_unicode );
2115 #if defined(HAVE_FRIBIDI)
2118 free( pi_karaoke_bar );
2119 return VLC_EGENERIC;
2124 p_result->x = __MAX( p_result->x, tmp_result.x );
2125 p_result->y += tmp_result.y;
2130 if( *psz_unicode == '\n')
2134 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2136 *c_ptr = *(c_ptr+1);
2141 free( psz_unicode );
2142 if( p_face ) FT_Done_Face( p_face );
2146 free( pp_char_styles );
2147 #if defined(HAVE_FRIBIDI)
2152 p_result->x = __MAX( p_result->x, tmp_result.x );
2153 p_result->y += tmp_result.y;
2156 if( pi_karaoke_bar )
2159 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2161 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2163 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2167 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2169 /* 100% BG colour will render faster if we
2170 * instead make it 100% FG colour, so leave
2171 * the ratio alone and copy the value across
2173 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2177 if( pi_karaoke_bar[ i ] & 0x80 )
2179 /* Swap Left and Right sides over for Right aligned
2180 * language text (eg. Arabic, Hebrew)
2182 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2184 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2185 p_line->p_bg_rgb[ k ] = i_tmp;
2187 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2190 /* Jump over the '\n' at the line-end */
2193 free( pi_karaoke_bar );
2199 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2200 subpicture_region_t *p_region_in )
2202 int rv = VLC_SUCCESS;
2203 stream_t *p_sub = NULL;
2204 xml_reader_t *p_xml_reader = NULL;
2206 if( !p_region_in || !p_region_in->psz_html )
2207 return VLC_EGENERIC;
2209 /* Reset the default fontsize in case screen metrics have changed */
2210 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2212 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2213 (uint8_t *) p_region_in->psz_html,
2214 strlen( p_region_in->psz_html ),
2218 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2219 if( p_filter->p_sys->p_xml )
2221 bool b_karaoke = false;
2223 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2226 /* Look for Root Node */
2227 if( xml_ReaderRead( p_xml_reader ) == 1 )
2229 char *psz_node = xml_ReaderName( p_xml_reader );
2231 if( !strcasecmp( "karaoke", psz_node ) )
2233 /* We're going to have to render the text a number
2234 * of times to show the progress marker on the text.
2236 var_SetBool( p_filter, "text-rerender", true );
2239 else if( !strcasecmp( "text", psz_node ) )
2245 /* Only text and karaoke tags are supported */
2246 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2247 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2248 p_xml_reader = NULL;
2260 uint32_t i_runs = 0;
2261 uint32_t i_k_runs = 0;
2262 uint32_t *pi_run_lengths = NULL;
2263 uint32_t *pi_k_run_lengths = NULL;
2264 uint32_t *pi_k_durations = NULL;
2265 ft_style_t **pp_styles = NULL;
2267 line_desc_t *p_lines = NULL;
2269 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2270 sizeof( uint32_t ) );
2275 rv = ProcessNodes( p_filter, p_xml_reader,
2276 p_region_in->p_style, psz_text, &i_len,
2277 &i_runs, &pi_run_lengths, &pp_styles,
2279 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2282 p_region_out->i_x = p_region_in->i_x;
2283 p_region_out->i_y = p_region_in->i_y;
2285 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2287 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2288 pi_run_lengths, pp_styles, &p_lines, &result,
2289 b_karaoke, i_k_runs, pi_k_run_lengths,
2293 for( k=0; k<i_runs; k++)
2294 DeleteStyle( pp_styles[k] );
2296 free( pi_run_lengths );
2299 /* Don't attempt to render text that couldn't be layed out
2302 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2304 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2306 Render( p_filter, p_region_out, p_lines,
2307 result.x, result.y );
2311 RenderYUVA( p_filter, p_region_out, p_lines,
2312 result.x, result.y );
2316 FreeLines( p_lines );
2318 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2321 stream_Delete( p_sub );
2327 static char* FontConfig_Select( FcConfig* priv, const char* family,
2328 bool b_bold, bool b_italic, int *i_idx )
2331 FcPattern *pat, *p_pat;
2335 pat = FcPatternCreate();
2336 if (!pat) return NULL;
2338 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2339 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2340 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2341 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2343 FcDefaultSubstitute( pat );
2345 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2347 FcPatternDestroy( pat );
2351 p_pat = FcFontMatch( priv, pat, &result );
2352 FcPatternDestroy( pat );
2353 if( !p_pat ) return NULL;
2355 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2356 || ( val_b != FcTrue ) )
2358 FcPatternDestroy( p_pat );
2361 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2366 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2368 FcPatternDestroy( p_pat );
2373 if( strcasecmp((const char*)val_s, family ) != 0 )
2374 msg_Warn( p_filter, "fontconfig: selected font family is not"
2375 "the requested one: '%s' != '%s'\n",
2376 (const char*)val_s, family );
2379 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2381 FcPatternDestroy( p_pat );
2385 FcPatternDestroy( p_pat );
2386 return strdup( (const char*)val_s );
2390 static void FreeLine( line_desc_t *p_line )
2393 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2395 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2397 free( p_line->pp_glyphs );
2398 free( p_line->p_glyph_pos );
2399 free( p_line->p_fg_rgb );
2400 free( p_line->p_bg_rgb );
2401 free( p_line->p_fg_bg_ratio );
2402 free( p_line->pi_underline_offset );
2403 free( p_line->pi_underline_thickness );
2407 static void FreeLines( line_desc_t *p_lines )
2409 line_desc_t *p_line, *p_next;
2411 if( !p_lines ) return;
2413 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2415 p_next = p_line->p_next;
2420 static line_desc_t *NewLine( int i_count )
2422 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2424 if( !p_line ) return NULL;
2425 p_line->i_height = 0;
2426 p_line->i_width = 0;
2427 p_line->p_next = NULL;
2429 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2430 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2431 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2432 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2433 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2434 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2435 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2436 if( ( p_line->pp_glyphs == NULL ) ||
2437 ( p_line->p_glyph_pos == NULL ) ||
2438 ( p_line->p_fg_rgb == NULL ) ||
2439 ( p_line->p_bg_rgb == NULL ) ||
2440 ( p_line->p_fg_bg_ratio == NULL ) ||
2441 ( p_line->pi_underline_offset == NULL ) ||
2442 ( p_line->pi_underline_thickness == NULL ) )
2444 free( p_line->pi_underline_thickness );
2445 free( p_line->pi_underline_offset );
2446 free( p_line->p_fg_rgb );
2447 free( p_line->p_bg_rgb );
2448 free( p_line->p_fg_bg_ratio );
2449 free( p_line->p_glyph_pos );
2450 free( p_line->pp_glyphs );
2454 p_line->pp_glyphs[0] = NULL;
2455 p_line->b_new_color_mode = false;
2460 static int GetFontSize( filter_t *p_filter )
2462 filter_sys_t *p_sys = p_filter->p_sys;
2466 if( p_sys->i_default_font_size )
2468 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2469 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2471 i_size = p_sys->i_default_font_size;
2475 var_Get( p_filter, "freetype-rel-fontsize", &val );
2478 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2479 p_filter->p_sys->i_display_height =
2480 p_filter->fmt_out.video.i_height;
2485 msg_Warn( p_filter, "invalid fontsize, using 12" );
2486 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2487 i_size = 12 * val.i_int / 1000;
2494 static int SetFontSize( filter_t *p_filter, int i_size )
2496 filter_sys_t *p_sys = p_filter->p_sys;
2500 i_size = GetFontSize( p_filter );
2502 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2505 p_sys->i_font_size = i_size;
2507 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2509 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2510 return VLC_EGENERIC;
2516 static void YUVFromRGB( uint32_t i_argb,
2517 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2519 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2520 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2521 int i_blue = ( i_argb & 0x000000ff );
2523 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2524 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2525 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2526 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2527 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2528 -585 * i_blue + 4096 + 1048576) >> 13, 240);