1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
42 #include <vlc_dialog.h>
43 #include <vlc_memory.h>
48 #include <freetype/ftsynth.h>
49 #include FT_FREETYPE_H
51 #define FT_FLOOR(X) ((X & -64) >> 6)
52 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
54 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
59 #define FC_DEFAULT_FONT "Arial Black"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
66 #elif defined( HAVE_MAEMO )
67 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
68 #define FC_DEFAULT_FONT "Nokia Sans Bold"
70 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
71 #define FC_DEFAULT_FONT "Serif Bold"
74 #if defined(HAVE_FRIBIDI)
75 #include <fribidi/fribidi.h>
78 #ifdef HAVE_FONTCONFIG
79 #include <fontconfig/fontconfig.h>
81 #define DEFAULT_FONT FC_DEFAULT_FONT
86 /*****************************************************************************
88 *****************************************************************************/
89 static int Create ( vlc_object_t * );
90 static void Destroy( vlc_object_t * );
92 #define FONT_TEXT N_("Font")
94 #ifdef HAVE_FONTCONFIG
95 #define FONT_LONGTEXT N_("Font family for the font you want to use")
97 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
100 #define FONTSIZE_TEXT N_("Font size in pixels")
101 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
102 "that will be rendered on the video. " \
103 "If set to something different than 0 this option will override the " \
104 "relative font size." )
105 #define OPACITY_TEXT N_("Opacity")
106 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
107 "text that will be rendered on the video. 0 = transparent, " \
108 "255 = totally opaque. " )
109 #define COLOR_TEXT N_("Text default color")
110 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
111 "the video. This must be an hexadecimal (like HTML colors). The first two "\
112 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
113 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
114 #define FONTSIZER_TEXT N_("Relative font size")
115 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
116 "fonts that will be rendered on the video. If absolute font size is set, "\
117 "relative size will be overridden." )
119 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
120 static const char *const ppsz_sizes_text[] = {
121 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
122 #define YUVP_TEXT N_("Use YUVP renderer")
123 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
124 "This option is only needed if you want to encode into DVB subtitles" )
125 #define EFFECT_TEXT N_("Font Effect")
126 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
127 "text to improve its readability." )
129 #define EFFECT_BACKGROUND 1
130 #define EFFECT_OUTLINE 2
131 #define EFFECT_OUTLINE_FAT 3
133 static int const pi_effects[] = { 1, 2, 3 };
134 static const char *const ppsz_effects_text[] = {
135 N_("Background"),N_("Outline"), N_("Fat Outline") };
136 static const int pi_color_values[] = {
137 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
138 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
139 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
141 static const char *const ppsz_color_descriptions[] = {
142 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
143 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
144 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
147 set_shortname( N_("Text renderer"))
148 set_description( N_("Freetype2 font renderer") )
149 set_category( CAT_VIDEO )
150 set_subcategory( SUBCAT_VIDEO_SUBPIC )
152 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
155 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
156 FONTSIZE_LONGTEXT, true )
158 /* opacity valid on 0..255, with default 255 = fully opaque */
159 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
160 OPACITY_TEXT, OPACITY_LONGTEXT, true )
162 /* hook to the color values list, with default 0x00ffffff = white */
163 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
164 COLOR_LONGTEXT, false )
165 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
167 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
168 FONTSIZER_LONGTEXT, false )
169 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
170 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
171 EFFECT_LONGTEXT, false )
172 change_integer_list( pi_effects, ppsz_effects_text, NULL )
174 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
175 YUVP_LONGTEXT, true )
176 set_capability( "text renderer", 100 )
177 add_shortcut( "text" )
178 set_callbacks( Create, Destroy )
183 /*****************************************************************************
185 *****************************************************************************/
187 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
188 static int RenderText( filter_t *, subpicture_region_t *,
189 subpicture_region_t * );
190 #ifdef HAVE_FONTCONFIG
191 static int RenderHtml( filter_t *, subpicture_region_t *,
192 subpicture_region_t * );
193 static char *FontConfig_Select( FcConfig *, const char *,
198 static int LoadFontsFromAttachments( filter_t *p_filter );
200 static int GetFontSize( filter_t *p_filter );
201 static int SetFontSize( filter_t *, int );
202 static void YUVFromRGB( uint32_t i_argb,
203 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
205 typedef struct line_desc_t line_desc_t;
208 /** NULL-terminated list of glyphs making the string */
209 FT_BitmapGlyph *pp_glyphs;
210 /** list of relative positions for the glyphs */
211 FT_Vector *p_glyph_pos;
212 /** list of RGB information for styled text
213 * -- if the rendering mode supports it (RenderYUVA) and
214 * b_new_color_mode is set, then it becomes possible to
215 * have multicoloured text within the subtitles. */
218 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
219 bool b_new_color_mode;
220 /** underline information -- only supplied if text should be underlined */
221 int *pi_underline_offset;
222 uint16_t *pi_underline_thickness;
226 int i_red, i_green, i_blue;
231 static line_desc_t *NewLine( int );
236 uint32_t i_font_color; /* ARGB */
237 uint32_t i_karaoke_bg_color; /* ARGB */
245 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
246 static void FreeLines( line_desc_t * );
247 static void FreeLine( line_desc_t * );
249 /*****************************************************************************
250 * filter_sys_t: freetype local data
251 *****************************************************************************
252 * This structure is part of the video output thread descriptor.
253 * It describes the freetype specific properties of an output thread.
254 *****************************************************************************/
257 FT_Library p_library; /* handle to library */
258 FT_Face p_face; /* handle to face object */
260 uint8_t i_font_opacity;
265 int i_default_font_size;
266 int i_display_height;
267 #ifdef HAVE_FONTCONFIG
268 char* psz_fontfamily;
272 input_attachment_t **pp_font_attachments;
273 int i_font_attachments;
277 #define UCHAR uint32_t
278 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
279 #define TR_FONT_STYLE_PTR ft_style_t *
281 #include "text_renderer.h"
283 /*****************************************************************************
284 * Create: allocates osd-text video thread output method
285 *****************************************************************************
286 * This function allocates and initializes a Clone vout method.
287 *****************************************************************************/
288 static int Create( vlc_object_t *p_this )
290 filter_t *p_filter = (filter_t *)p_this;
292 char *psz_fontfile=NULL;
293 char *psz_fontfamily=NULL;
294 int i_error,fontindex;
296 #ifdef HAVE_FONTCONFIG
297 FcPattern *fontpattern = NULL, *fontmatch = NULL;
298 /* Initialise result to Match, as fontconfig doesnt
299 * really set this other than some error-cases */
300 FcResult fontresult = FcResultMatch;
304 /* Allocate structure */
305 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
308 #ifdef HAVE_FONTCONFIG
309 p_sys->psz_fontfamily = NULL;
313 p_sys->p_library = 0;
314 p_sys->i_font_size = 0;
315 p_sys->i_display_height = 0;
317 var_Create( p_filter, "freetype-rel-fontsize",
318 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
320 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
321 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
322 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
323 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
324 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
325 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
326 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
329 if( !psz_fontfamily || !*psz_fontfamily )
331 free( psz_fontfamily );
332 #ifdef HAVE_FONTCONFIG
333 psz_fontfamily=strdup( DEFAULT_FONT );
335 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
336 if( !psz_fontfamily )
339 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
340 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
342 strcpy( psz_fontfamily, DEFAULT_FONT );
344 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
348 #ifdef HAVE_FONTCONFIG
349 /* Lets find some fontfile from freetype-font variable family */
351 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
355 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
356 _("Building font cache"),
357 _("Please wait while your font cache is rebuilt.\n"
358 "This should take less than a few minutes."), NULL );
359 char *path = xmalloc( PATH_MAX + 1 );
360 /* Fontconfig doesnt seem to know where windows fonts are with
361 * current contribs. So just tell default windows font directory
362 * is the place to search fonts
364 GetWindowsDirectory( path, PATH_MAX + 1 );
365 strcat( path, "\\fonts" );
367 dialog_ProgressSet( p_dialog, NULL, 0.4 );
369 FcConfigAppFontAddDir( NULL , path );
374 dialog_ProgressSet( p_dialog, NULL, 0.5 );
378 msg_Dbg( p_filter, "Building font database.");
380 FcConfigBuildFonts( NULL );
383 msg_Dbg( p_filter, "Finished building font database." );
384 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
386 fontpattern = FcPatternCreate();
390 msg_Err( p_filter, "Creating fontpattern failed");
396 dialog_ProgressSet( p_dialog, NULL, 0.7 );
398 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
399 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
400 free( psz_fontsize );
402 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
404 msg_Err( p_filter, "FontSubstitute failed");
407 FcDefaultSubstitute( fontpattern );
411 dialog_ProgressSet( p_dialog, NULL, 0.8 );
413 /* testing fontresult here doesn't do any good really, but maybe it will
414 * in future as fontconfig code doesn't set it in all cases and just
415 * returns NULL or doesn't set to to Match on all Match cases.*/
416 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
417 if( !fontmatch || fontresult == FcResultNoMatch )
419 msg_Err( p_filter, "Fontmatching failed");
423 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
424 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
427 msg_Err( p_filter, "Failed to get fontfile");
431 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
432 psz_fontfile ? psz_fontfile : "(null)" );
433 p_sys->psz_fontfamily = strdup( psz_fontfamily );
437 dialog_ProgressSet( p_dialog, NULL, 1.0 );
438 dialog_ProgressDestroy( p_dialog );
445 #ifdef HAVE_FONTCONFIG
446 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
447 psz_fontfile = psz_fontfamily;
452 i_error = FT_Init_FreeType( &p_sys->p_library );
455 msg_Err( p_filter, "couldn't initialize freetype" );
459 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
460 fontindex, &p_sys->p_face );
462 if( i_error == FT_Err_Unknown_File_Format )
464 msg_Err( p_filter, "file %s have unknown format",
465 psz_fontfile ? psz_fontfile : "(null)" );
470 msg_Err( p_filter, "failed to load font file %s",
471 psz_fontfile ? psz_fontfile : "(null)" );
475 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
478 msg_Err( p_filter, "font has no unicode translation table" );
482 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
484 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
487 p_sys->pp_font_attachments = NULL;
488 p_sys->i_font_attachments = 0;
490 p_filter->pf_render_text = RenderText;
491 #ifdef HAVE_FONTCONFIG
492 p_filter->pf_render_html = RenderHtml;
493 FcPatternDestroy( fontmatch );
494 FcPatternDestroy( fontpattern );
496 p_filter->pf_render_html = NULL;
499 free( psz_fontfamily );
500 LoadFontsFromAttachments( p_filter );
505 #ifdef HAVE_FONTCONFIG
506 if( fontmatch ) FcPatternDestroy( fontmatch );
507 if( fontpattern ) FcPatternDestroy( fontpattern );
512 dialog_ProgressDestroy( p_dialog );
515 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
516 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
517 free( psz_fontfamily );
522 /*****************************************************************************
523 * Destroy: destroy Clone video thread output method
524 *****************************************************************************
525 * Clean up all data and library connections
526 *****************************************************************************/
527 static void Destroy( vlc_object_t *p_this )
529 filter_t *p_filter = (filter_t *)p_this;
530 filter_sys_t *p_sys = p_filter->p_sys;
532 if( p_sys->pp_font_attachments )
536 for( k = 0; k < p_sys->i_font_attachments; k++ )
537 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
539 free( p_sys->pp_font_attachments );
542 #ifdef HAVE_FONTCONFIG
543 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
544 free( p_sys->psz_fontfamily );
547 /* FcFini asserts calling the subfunction FcCacheFini()
548 * even if no other library functions have been made since FcInit(),
549 * so don't call it. */
551 FT_Done_Face( p_sys->p_face );
552 FT_Done_FreeType( p_sys->p_library );
556 /*****************************************************************************
557 * Make any TTF/OTF fonts present in the attachments of the media file
558 * and store them for later use by the FreeType Engine
559 *****************************************************************************/
560 static int LoadFontsFromAttachments( filter_t *p_filter )
562 filter_sys_t *p_sys = p_filter->p_sys;
563 input_thread_t *p_input;
564 input_attachment_t **pp_attachments;
565 int i_attachments_cnt;
567 int rv = VLC_SUCCESS;
569 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
573 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
575 vlc_object_release(p_input);
579 p_sys->i_font_attachments = 0;
580 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
581 if(! p_sys->pp_font_attachments )
584 for( k = 0; k < i_attachments_cnt; k++ )
586 input_attachment_t *p_attach = pp_attachments[k];
588 if( p_sys->pp_font_attachments )
590 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
591 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
592 ( p_attach->i_data > 0 ) &&
593 ( p_attach->p_data != NULL ) )
595 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
599 vlc_input_attachment_Delete( p_attach );
604 vlc_input_attachment_Delete( p_attach );
607 free( pp_attachments );
609 vlc_object_release(p_input);
614 /*****************************************************************************
615 * Render: place string in picture
616 *****************************************************************************
617 * This function merges the previously rendered freetype glyphs into a picture
618 *****************************************************************************/
619 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
620 line_desc_t *p_line, int i_width, int i_height )
622 VLC_UNUSED(p_filter);
623 static const uint8_t pi_gamma[16] =
624 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
625 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
629 int i, x, y, i_pitch;
630 uint8_t i_y; /* YUV values, derived from incoming RGB */
633 /* Create a new subpicture region */
634 memset( &fmt, 0, sizeof(video_format_t) );
635 fmt.i_chroma = VLC_CODEC_YUVP;
636 fmt.i_width = fmt.i_visible_width = i_width + 4;
637 fmt.i_height = fmt.i_visible_height = i_height + 4;
638 if( p_region->fmt.i_visible_width > 0 )
639 fmt.i_visible_width = p_region->fmt.i_visible_width;
640 if( p_region->fmt.i_visible_height > 0 )
641 fmt.i_visible_height = p_region->fmt.i_visible_height;
642 fmt.i_x_offset = fmt.i_y_offset = 0;
644 assert( !p_region->p_picture );
645 p_region->p_picture = picture_NewFromFormat( &fmt );
646 if( !p_region->p_picture )
648 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
651 /* Calculate text color components */
652 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
653 25 * p_line->i_blue + 128) >> 8) + 16;
654 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
655 112 * p_line->i_blue + 128) >> 8) + 128;
656 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
657 18 * p_line->i_blue + 128) >> 8) + 128;
660 fmt.p_palette->i_entries = 16;
661 for( i = 0; i < 8; i++ )
663 fmt.p_palette->palette[i][0] = 0;
664 fmt.p_palette->palette[i][1] = 0x80;
665 fmt.p_palette->palette[i][2] = 0x80;
666 fmt.p_palette->palette[i][3] = pi_gamma[i];
667 fmt.p_palette->palette[i][3] =
668 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
670 for( i = 8; i < fmt.p_palette->i_entries; i++ )
672 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
673 fmt.p_palette->palette[i][1] = i_u;
674 fmt.p_palette->palette[i][2] = i_v;
675 fmt.p_palette->palette[i][3] = pi_gamma[i];
676 fmt.p_palette->palette[i][3] =
677 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
680 p_dst = p_region->p_picture->Y_PIXELS;
681 i_pitch = p_region->p_picture->Y_PITCH;
683 /* Initialize the region pixels */
684 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
686 for( ; p_line != NULL; p_line = p_line->p_next )
688 int i_glyph_tmax = 0;
689 int i_bitmap_offset, i_offset, i_align_offset = 0;
690 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
692 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
693 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
696 if( p_line->i_width < i_width )
698 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
700 i_align_offset = i_width - p_line->i_width;
702 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
704 i_align_offset = ( i_width - p_line->i_width ) / 2;
708 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
710 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
712 i_offset = ( p_line->p_glyph_pos[ i ].y +
713 i_glyph_tmax - p_glyph->top + 2 ) *
714 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
717 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
719 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
721 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
723 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
730 /* Outlining (find something better than nearest neighbour filtering ?) */
733 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
734 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
735 uint8_t left, current;
737 for( y = 1; y < (int)fmt.i_height - 1; y++ )
739 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
740 p_dst += p_region->p_picture->Y_PITCH;
743 for( x = 1; x < (int)fmt.i_width - 1; x++ )
746 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
747 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;
751 memset( p_top, 0, fmt.i_width );
757 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
758 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
759 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
760 int i_glyph_tmax, int i_align_offset,
761 uint8_t i_y, uint8_t i_u, uint8_t i_v,
762 subpicture_region_t *p_region)
766 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
768 p_dst_y = p_region->p_picture->Y_PIXELS;
769 p_dst_u = p_region->p_picture->U_PIXELS;
770 p_dst_v = p_region->p_picture->V_PIXELS;
771 p_dst_a = p_region->p_picture->A_PIXELS;
772 i_pitch = p_region->p_picture->A_PITCH;
774 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
775 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
777 for( y = 0; y < i_line_thickness; y++ )
779 int i_extra = p_this_glyph->bitmap.width;
783 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
784 (p_this_glyph_pos->x + p_this_glyph->left);
786 for( x = 0; x < i_extra; x++ )
790 /* break the underline around the tails of any glyphs which cross it */
791 /* Strikethrough doesn't get broken */
792 for( z = x - i_line_thickness;
793 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
796 if( p_next_glyph && ( z >= i_extra ) )
798 int i_row = i_line_offset + p_next_glyph->top + y;
800 if( ( p_next_glyph->bitmap.rows > i_row ) &&
801 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
806 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
808 int i_row = i_line_offset + p_this_glyph->top + y;
810 if( ( p_this_glyph->bitmap.rows > i_row ) &&
811 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
820 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
821 p_dst_u[i_offset+x] = i_u;
822 p_dst_v[i_offset+x] = i_v;
823 p_dst_a[i_offset+x] = 255;
830 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
832 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
833 int i_pitch = p_region->p_picture->A_PITCH;
836 for( ; p_line != NULL; p_line = p_line->p_next )
838 int i_glyph_tmax=0, i = 0;
839 int i_bitmap_offset, i_offset, i_align_offset = 0;
840 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
842 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
843 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
846 if( p_line->i_width < i_width )
848 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
850 i_align_offset = i_width - p_line->i_width;
852 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
854 i_align_offset = ( i_width - p_line->i_width ) / 2;
858 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
860 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
862 i_offset = ( p_line->p_glyph_pos[ i ].y +
863 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
864 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
865 i_align_offset +xoffset;
867 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
869 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
871 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
872 if( p_dst[i_offset+x] <
873 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
875 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
884 /*****************************************************************************
885 * Render: place string in picture
886 *****************************************************************************
887 * This function merges the previously rendered freetype glyphs into a picture
888 *****************************************************************************/
889 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
890 line_desc_t *p_line, int i_width, int i_height )
892 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
894 int i, x, y, i_pitch, i_alpha;
895 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
897 if( i_width == 0 || i_height == 0 )
900 /* Create a new subpicture region */
901 memset( &fmt, 0, sizeof(video_format_t) );
902 fmt.i_chroma = VLC_CODEC_YUVA;
903 fmt.i_width = fmt.i_visible_width = i_width + 6;
904 fmt.i_height = fmt.i_visible_height = i_height + 6;
905 if( p_region->fmt.i_visible_width > 0 )
906 fmt.i_visible_width = p_region->fmt.i_visible_width;
907 if( p_region->fmt.i_visible_height > 0 )
908 fmt.i_visible_height = p_region->fmt.i_visible_height;
909 fmt.i_x_offset = fmt.i_y_offset = 0;
911 p_region->p_picture = picture_NewFromFormat( &fmt );
912 if( !p_region->p_picture )
916 /* Calculate text color components */
917 YUVFromRGB( (p_line->i_red << 16) |
918 (p_line->i_green << 8) |
921 i_alpha = p_line->i_alpha;
923 p_dst_y = p_region->p_picture->Y_PIXELS;
924 p_dst_u = p_region->p_picture->U_PIXELS;
925 p_dst_v = p_region->p_picture->V_PIXELS;
926 p_dst_a = p_region->p_picture->A_PIXELS;
927 i_pitch = p_region->p_picture->A_PITCH;
929 /* Initialize the region pixels */
930 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
932 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
933 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
934 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
935 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
939 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
940 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
941 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
942 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
944 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
945 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
947 DrawBlack( p_line, i_width, p_region, 0, 0);
948 DrawBlack( p_line, i_width, p_region, -1, 0);
949 DrawBlack( p_line, i_width, p_region, 0, -1);
950 DrawBlack( p_line, i_width, p_region, 1, 0);
951 DrawBlack( p_line, i_width, p_region, 0, 1);
954 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
956 DrawBlack( p_line, i_width, p_region, -1, -1);
957 DrawBlack( p_line, i_width, p_region, -1, 1);
958 DrawBlack( p_line, i_width, p_region, 1, -1);
959 DrawBlack( p_line, i_width, p_region, 1, 1);
961 DrawBlack( p_line, i_width, p_region, -2, 0);
962 DrawBlack( p_line, i_width, p_region, 0, -2);
963 DrawBlack( p_line, i_width, p_region, 2, 0);
964 DrawBlack( p_line, i_width, p_region, 0, 2);
966 DrawBlack( p_line, i_width, p_region, -2, -2);
967 DrawBlack( p_line, i_width, p_region, -2, 2);
968 DrawBlack( p_line, i_width, p_region, 2, -2);
969 DrawBlack( p_line, i_width, p_region, 2, 2);
971 DrawBlack( p_line, i_width, p_region, -3, 0);
972 DrawBlack( p_line, i_width, p_region, 0, -3);
973 DrawBlack( p_line, i_width, p_region, 3, 0);
974 DrawBlack( p_line, i_width, p_region, 0, 3);
977 for( ; p_line != NULL; p_line = p_line->p_next )
979 int i_glyph_tmax = 0;
980 int i_bitmap_offset, i_offset, i_align_offset = 0;
981 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
983 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
984 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
987 if( p_line->i_width < i_width )
989 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
991 i_align_offset = i_width - p_line->i_width;
993 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
995 i_align_offset = ( i_width - p_line->i_width ) / 2;
999 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1001 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1003 i_offset = ( p_line->p_glyph_pos[ i ].y +
1004 i_glyph_tmax - p_glyph->top + 3 ) *
1005 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1008 if( p_line->b_new_color_mode )
1010 /* Every glyph can (and in fact must) have its own color */
1011 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1014 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1016 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1018 uint8_t i_y_local = i_y;
1019 uint8_t i_u_local = i_u;
1020 uint8_t i_v_local = i_v;
1022 if( p_line->p_fg_bg_ratio != 0x00 )
1024 int i_split = p_glyph->bitmap.width *
1025 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1029 YUVFromRGB( p_line->p_bg_rgb[ i ],
1030 &i_y_local, &i_u_local, &i_v_local );
1034 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1036 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1037 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1039 p_dst_u[i_offset+x] = i_u;
1040 p_dst_v[i_offset+x] = i_v;
1042 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1043 p_dst_a[i_offset+x] = 0xff;
1046 i_offset += i_pitch;
1049 if( p_line->pi_underline_thickness[ i ] )
1051 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1052 p_line->pi_underline_offset[ i ],
1053 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1054 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1055 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1056 i_glyph_tmax, i_align_offset,
1063 /* Apply the alpha setting */
1064 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1065 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1071 * This function renders a text subpicture region into another one.
1072 * It also calculates the size needed for this string, and renders the
1073 * needed glyphs into memory. It is used as pf_add_string callback in
1074 * the vout method by this module
1076 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1077 subpicture_region_t *p_region_in )
1079 filter_sys_t *p_sys = p_filter->p_sys;
1080 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1081 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1082 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1083 int i_string_length;
1085 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1086 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1096 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1097 psz_string = p_region_in->psz_text;
1098 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1100 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1101 i_scale = val.i_int;
1103 if( p_region_in->p_style )
1105 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1106 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1107 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1111 i_font_color = p_sys->i_font_color;
1112 i_font_alpha = 255 - p_sys->i_font_opacity;
1113 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1116 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1117 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1118 SetFontSize( p_filter, i_font_size );
1120 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1121 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1122 i_blue = i_font_color & 0x000000FF;
1124 result.x = result.y = 0;
1125 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1127 psz_unicode = psz_unicode_orig =
1128 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1129 if( psz_unicode == NULL )
1131 #if defined(WORDS_BIGENDIAN)
1132 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1134 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1136 if( iconv_handle == (vlc_iconv_t)-1 )
1138 msg_Warn( p_filter, "unable to do conversion" );
1144 const char *p_in_buffer = psz_string;
1145 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1146 i_in_bytes = strlen( psz_string );
1147 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1148 i_out_bytes_left = i_out_bytes;
1149 p_out_buffer = (char *)psz_unicode;
1150 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1152 &p_out_buffer, &i_out_bytes_left );
1154 vlc_iconv_close( iconv_handle );
1158 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1159 "bytes left %u", (unsigned)i_in_bytes );
1162 *(uint32_t*)p_out_buffer = 0;
1163 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1166 #if defined(HAVE_FRIBIDI)
1168 uint32_t *p_fribidi_string;
1169 int32_t start_pos, pos = 0;
1171 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1172 if( !p_fribidi_string )
1175 /* Do bidi conversion line-by-line */
1176 while( pos < i_string_length )
1178 while( pos < i_string_length )
1180 i_char = psz_unicode[pos];
1181 if (i_char != '\r' && i_char != '\n')
1183 p_fribidi_string[pos] = i_char;
1187 while( pos < i_string_length )
1189 i_char = psz_unicode[pos];
1190 if (i_char == '\r' || i_char == '\n')
1194 if (pos > start_pos)
1196 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1197 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1200 (FriBidiChar*)p_fribidi_string + start_pos,
1205 free( psz_unicode_orig );
1206 psz_unicode = psz_unicode_orig = p_fribidi_string;
1207 p_fribidi_string[ i_string_length ] = 0;
1211 /* Calculate relative glyph positions and a bounding box for the
1213 if( !(p_line = NewLine( strlen( psz_string ))) )
1216 i_pen_x = i_pen_y = 0;
1218 psz_line_start = psz_unicode;
1220 #define face p_sys->p_face
1221 #define glyph face->glyph
1223 while( *psz_unicode )
1225 i_char = *psz_unicode++;
1226 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1231 if( i_char == '\n' )
1233 psz_line_start = psz_unicode;
1234 if( !(p_next = NewLine( strlen( psz_string ))) )
1236 p_line->p_next = p_next;
1237 p_line->i_width = line.xMax;
1238 p_line->i_height = face->size->metrics.height >> 6;
1239 p_line->pp_glyphs[ i ] = NULL;
1240 p_line->i_alpha = i_font_alpha;
1241 p_line->i_red = i_red;
1242 p_line->i_green = i_green;
1243 p_line->i_blue = i_blue;
1246 result.x = __MAX( result.x, line.xMax );
1247 result.y += face->size->metrics.height >> 6;
1250 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1251 i_pen_y += face->size->metrics.height >> 6;
1253 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1258 i_glyph_index = FT_Get_Char_Index( face, i_char );
1259 if( p_sys->i_use_kerning && i_glyph_index
1263 FT_Get_Kerning( face, i_previous, i_glyph_index,
1264 ft_kerning_default, &delta );
1265 i_pen_x += delta.x >> 6;
1268 p_line->p_glyph_pos[ i ].x = i_pen_x;
1269 p_line->p_glyph_pos[ i ].y = i_pen_y;
1270 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1273 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1277 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1280 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1284 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1285 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1288 FT_Done_Glyph( tmp_glyph );
1291 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1294 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1295 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1296 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1298 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1299 p_line->pp_glyphs[ i ] = NULL;
1301 p_line = NewLine( strlen( psz_string ));
1302 if( p_prev ) p_prev->p_next = p_line;
1303 else p_lines = p_line;
1305 uint32_t *psz_unicode_saved = psz_unicode;
1306 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1310 if( psz_unicode == psz_line_start )
1311 { /* try harder to break that line */
1312 psz_unicode = psz_unicode_saved;
1313 while( psz_unicode > psz_line_start &&
1314 *psz_unicode != '_' && *psz_unicode != '/' &&
1315 *psz_unicode != '\\' && *psz_unicode != '.' )
1320 if( psz_unicode == psz_line_start )
1322 msg_Warn( p_filter, "unbreakable string" );
1327 *psz_unicode = '\n';
1329 psz_unicode = psz_line_start;
1332 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1335 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1336 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1338 i_previous = i_glyph_index;
1339 i_pen_x += glyph->advance.x >> 6;
1343 p_line->i_width = line.xMax;
1344 p_line->i_height = face->size->metrics.height >> 6;
1345 p_line->pp_glyphs[ i ] = NULL;
1346 p_line->i_alpha = i_font_alpha;
1347 p_line->i_red = i_red;
1348 p_line->i_green = i_green;
1349 p_line->i_blue = i_blue;
1350 result.x = __MAX( result.x, line.xMax );
1351 result.y += line.yMax - line.yMin;
1356 p_region_out->i_x = p_region_in->i_x;
1357 p_region_out->i_y = p_region_in->i_y;
1359 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1360 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1362 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1364 free( psz_unicode_orig );
1365 FreeLines( p_lines );
1369 free( psz_unicode_orig );
1370 FreeLines( p_lines );
1371 return VLC_EGENERIC;
1374 #ifdef HAVE_FONTCONFIG
1375 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1376 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1377 bool b_italic, bool b_uline, bool b_through )
1379 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1383 p_style->i_font_size = i_font_size;
1384 p_style->i_font_color = i_font_color;
1385 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1386 p_style->b_italic = b_italic;
1387 p_style->b_bold = b_bold;
1388 p_style->b_underline = b_uline;
1389 p_style->b_through = b_through;
1391 p_style->psz_fontname = strdup( psz_fontname );
1396 static void DeleteStyle( ft_style_t *p_style )
1400 free( p_style->psz_fontname );
1405 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1412 if(( s1->i_font_size == s2->i_font_size ) &&
1413 ( s1->i_font_color == s2->i_font_color ) &&
1414 ( s1->b_italic == s2->b_italic ) &&
1415 ( s1->b_through == s2->b_through ) &&
1416 ( s1->b_bold == s2->b_bold ) &&
1417 ( s1->b_underline == s2->b_underline ) &&
1418 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1425 static void IconvText( filter_t *p_filter, const char *psz_string,
1426 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1428 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1430 /* If memory hasn't been allocated for our output string, allocate it here
1431 * - the calling function must now be responsible for freeing it.
1433 if( !*ppsz_unicode )
1434 *ppsz_unicode = (uint32_t *)
1435 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1437 /* We don't need to handle a NULL pointer in *ppsz_unicode
1438 * if we are instead testing for a non NULL value like we are here */
1442 #if defined(WORDS_BIGENDIAN)
1443 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1445 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1447 if( iconv_handle != (vlc_iconv_t)-1 )
1449 char *p_in_buffer, *p_out_buffer;
1450 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1451 i_in_bytes = strlen( psz_string );
1452 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1453 i_out_bytes_left = i_out_bytes;
1454 p_in_buffer = (char *) psz_string;
1455 p_out_buffer = (char *) *ppsz_unicode;
1456 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1457 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1459 vlc_iconv_close( iconv_handle );
1463 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1464 "bytes left %u", (unsigned)i_in_bytes );
1468 *(uint32_t*)p_out_buffer = 0;
1470 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1475 msg_Warn( p_filter, "unable to do conversion" );
1480 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1481 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1482 bool b_uline, bool b_through )
1484 ft_style_t *p_style = NULL;
1486 char *psz_fontname = NULL;
1487 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1488 uint32_t i_karaoke_bg_color = i_font_color;
1489 int i_font_size = p_sys->i_font_size;
1491 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1492 &i_font_color, &i_karaoke_bg_color ))
1494 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1495 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1500 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1501 bool b_uline, bool b_through, bool b_bold,
1502 bool b_italic, int i_karaoke_bgcolor,
1503 line_desc_t *p_line, uint32_t *psz_unicode,
1504 int *pi_pen_x, int i_pen_y, int *pi_start,
1505 FT_Vector *p_result )
1510 bool b_first_on_line = true;
1513 int i_pen_x_start = *pi_pen_x;
1515 uint32_t *psz_unicode_start = psz_unicode;
1517 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1519 /* Account for part of line already in position */
1520 for( i=0; i<*pi_start; i++ )
1524 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1525 ft_glyph_bbox_pixels, &glyph_size );
1527 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1528 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1529 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1530 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1536 b_first_on_line = false;
1538 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1544 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1545 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1549 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1550 ft_kerning_default, &delta );
1551 *pi_pen_x += delta.x >> 6;
1553 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1554 p_line->p_glyph_pos[ i ].y = i_pen_y;
1556 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1560 "unable to render text FT_Load_Glyph returned %d", i_error );
1561 p_line->pp_glyphs[ i ] = NULL;
1562 return VLC_EGENERIC;
1565 /* Do synthetic styling now that Freetype supports it;
1566 * ie. if the font we have loaded is NOT already in the
1567 * style that the tags want, then switch it on; if they
1568 * are then don't. */
1569 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1570 FT_GlyphSlot_Embolden( p_face->glyph );
1571 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1572 FT_GlyphSlot_Oblique( p_face->glyph );
1574 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1578 "unable to render text FT_Get_Glyph returned %d", i_error );
1579 p_line->pp_glyphs[ i ] = NULL;
1580 return VLC_EGENERIC;
1582 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1583 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1586 FT_Done_Glyph( tmp_glyph );
1589 if( b_uline || b_through )
1591 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1592 p_face->size->metrics.y_scale));
1593 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1594 p_face->size->metrics.y_scale));
1596 p_line->pi_underline_offset[ i ] =
1597 ( aOffset < 0 ) ? -aOffset : aOffset;
1598 p_line->pi_underline_thickness[ i ] =
1599 ( aSize < 0 ) ? -aSize : aSize;
1602 /* Move the baseline to make it strikethrough instead of
1603 * underline. That means that strikethrough takes precedence
1605 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1606 p_face->size->metrics.y_scale));
1608 p_line->pi_underline_offset[ i ] -=
1609 ( aDescent < 0 ) ? -aDescent : aDescent;
1613 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1614 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1615 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1616 p_line->p_fg_bg_ratio[ i ] = 0x00;
1618 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1619 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1620 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1622 for( ; i >= *pi_start; i-- )
1623 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1626 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1630 if( psz_unicode == psz_unicode_start )
1632 if( b_first_on_line )
1634 msg_Warn( p_filter, "unbreakable string" );
1635 p_line->pp_glyphs[ i ] = NULL;
1636 return VLC_EGENERIC;
1638 *pi_pen_x = i_pen_x_start;
1640 p_line->i_width = line.xMax;
1641 p_line->i_height = __MAX( p_line->i_height,
1642 p_face->size->metrics.height >> 6 );
1643 p_line->pp_glyphs[ i ] = NULL;
1645 p_result->x = __MAX( p_result->x, line.xMax );
1646 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1647 i_yMax - i_yMin ) );
1652 *psz_unicode = '\n';
1654 psz_unicode = psz_unicode_start;
1655 *pi_pen_x = i_pen_x_start;
1663 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1664 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1666 i_previous = i_glyph_index;
1667 *pi_pen_x += p_face->glyph->advance.x >> 6;
1670 p_line->i_width = line.xMax;
1671 p_line->i_height = __MAX( p_line->i_height,
1672 p_face->size->metrics.height >> 6 );
1673 p_line->pp_glyphs[ i ] = NULL;
1675 p_result->x = __MAX( p_result->x, line.xMax );
1676 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1677 line.yMax - line.yMin ) );
1681 /* Get rid of any text processed - if necessary repositioning
1682 * at the start of a new line of text
1686 *psz_unicode_start = '\0';
1688 else if( psz_unicode > psz_unicode_start )
1690 for( i=0; psz_unicode[ i ]; i++ )
1691 psz_unicode_start[ i ] = psz_unicode[ i ];
1692 psz_unicode_start[ i ] = '\0';
1698 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1699 uint32_t **psz_text_out, uint32_t *pi_runs,
1700 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1701 ft_style_t *p_style )
1703 uint32_t i_string_length = 0;
1705 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1706 *psz_text_out += i_string_length;
1708 if( ppp_styles && ppi_run_lengths )
1712 /* XXX this logic looks somewhat broken */
1716 *ppp_styles = realloc_or_free( *ppp_styles,
1717 *pi_runs * sizeof( ft_style_t * ) );
1719 else if( *pi_runs == 1 )
1721 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1724 /* We have just malloc'ed this memory successfully -
1725 * *pi_runs HAS to be within the memory area of *ppp_styles */
1728 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1732 /* XXX more iffy logic */
1734 if( *ppi_run_lengths )
1736 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1737 *pi_runs * sizeof( uint32_t ) );
1739 else if( *pi_runs == 1 )
1741 *ppi_run_lengths = (uint32_t *)
1742 malloc( *pi_runs * sizeof( uint32_t ) );
1745 /* same remarks here */
1746 if( *ppi_run_lengths )
1748 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1751 /* If we couldn't use the p_style argument due to memory allocation
1752 * problems above, release it here.
1754 if( p_style ) DeleteStyle( p_style );
1757 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1761 for( k=0; k < p_sys->i_font_attachments; k++ )
1763 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1765 FT_Face p_face = NULL;
1767 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1775 bool match = !strcasecmp( p_face->family_name,
1776 p_style->psz_fontname );
1778 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1779 match = match && p_style->b_bold;
1781 match = match && !p_style->b_bold;
1783 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1784 match = match && p_style->b_italic;
1786 match = match && !p_style->b_italic;
1794 FT_Done_Face( p_face );
1799 return VLC_EGENERIC;
1802 static int ProcessLines( filter_t *p_filter,
1807 uint32_t *pi_run_lengths,
1808 ft_style_t **pp_styles,
1809 line_desc_t **pp_lines,
1811 FT_Vector *p_result,
1815 uint32_t *pi_k_run_lengths,
1816 uint32_t *pi_k_durations )
1818 filter_sys_t *p_sys = p_filter->p_sys;
1819 ft_style_t **pp_char_styles;
1820 int *p_new_positions = NULL;
1821 int8_t *p_levels = NULL;
1822 uint8_t *pi_karaoke_bar = NULL;
1826 /* Assign each character in the text string its style explicitly, so that
1827 * after the characters have been shuffled around by Fribidi, we can re-apply
1828 * the styles, and to simplify the calculation of runs within a line.
1830 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1831 if( !pp_char_styles )
1836 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1837 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1838 * we just won't be able to display the progress bar; at least we'll
1844 for( j = 0; j < i_runs; j++ )
1845 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1846 pp_char_styles[ i++ ] = pp_styles[ j ];
1848 #if defined(HAVE_FRIBIDI)
1850 ft_style_t **pp_char_styles_new;
1851 int *p_old_positions;
1852 uint32_t *p_fribidi_string;
1853 int start_pos, pos = 0;
1855 pp_char_styles_new = (ft_style_t **)
1856 malloc( i_len * sizeof( ft_style_t * ));
1858 p_fribidi_string = (uint32_t *)
1859 malloc( (i_len + 1) * sizeof(uint32_t) );
1860 p_old_positions = (int *)
1861 malloc( (i_len + 1) * sizeof( int ) );
1862 p_new_positions = (int *)
1863 malloc( (i_len + 1) * sizeof( int ) );
1864 p_levels = (int8_t *)
1865 malloc( (i_len + 1) * sizeof( int8_t ) );
1867 if( ! pp_char_styles_new ||
1868 ! p_fribidi_string ||
1869 ! p_old_positions ||
1870 ! p_new_positions ||
1874 free( p_old_positions );
1875 free( p_new_positions );
1876 free( p_fribidi_string );
1877 free( pp_char_styles_new );
1878 free( pi_karaoke_bar );
1880 free( pp_char_styles );
1884 /* Do bidi conversion line-by-line */
1887 while(pos < i_len) {
1888 if (psz_text[pos] != '\n')
1890 p_fribidi_string[pos] = psz_text[pos];
1891 pp_char_styles_new[pos] = pp_char_styles[pos];
1892 p_new_positions[pos] = pos;
1897 while(pos < i_len) {
1898 if (psz_text[pos] == '\n')
1902 if (pos > start_pos)
1904 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1905 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1906 pos - start_pos, &base_dir,
1907 (FriBidiChar*)p_fribidi_string + start_pos,
1908 p_new_positions + start_pos,
1910 p_levels + start_pos );
1911 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1913 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1914 p_old_positions[ j - start_pos ] ];
1915 p_new_positions[ j ] += start_pos;
1919 free( p_old_positions );
1920 free( pp_char_styles );
1921 pp_char_styles = pp_char_styles_new;
1922 psz_text = p_fribidi_string;
1923 p_fribidi_string[ i_len ] = 0;
1926 /* Work out the karaoke */
1927 if( pi_karaoke_bar )
1929 int64_t i_last_duration = 0;
1930 int64_t i_duration = 0;
1931 int64_t i_start_pos = 0;
1932 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1934 for( k = 0; k< i_k_runs; k++ )
1936 double fraction = 0.0;
1938 i_duration += pi_k_durations[ k ];
1940 if( i_duration < i_elapsed )
1942 /* Completely finished this run-length -
1943 * let it render normally */
1947 else if( i_elapsed < i_last_duration )
1949 /* Haven't got up to this segment yet -
1950 * render it completely in karaoke BG mode */
1956 /* Partway through this run */
1958 fraction = (double)(i_elapsed - i_last_duration) /
1959 (double)pi_k_durations[ k ];
1961 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1963 double shade = pi_k_run_lengths[ k ] * fraction;
1965 if( p_new_positions )
1966 j = p_new_positions[ i_start_pos + i ];
1968 j = i_start_pos + i;
1970 if( i < (uint32_t)shade )
1971 pi_karaoke_bar[ j ] = 0xff;
1972 else if( (double)i > shade )
1973 pi_karaoke_bar[ j ] = 0x00;
1976 shade -= (int)shade;
1977 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1978 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1982 i_last_duration = i_duration;
1983 i_start_pos += pi_k_run_lengths[ k ];
1987 free( p_new_positions );
1989 FT_Vector tmp_result;
1991 line_desc_t *p_line = NULL;
1992 line_desc_t *p_prev = NULL;
1998 p_result->x = p_result->y = 0;
1999 tmp_result.x = tmp_result.y = 0;
2002 for( k = 0; k <= (uint32_t) i_len; k++ )
2004 if( ( k == (uint32_t) i_len ) ||
2006 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2008 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2010 /* End of the current style run */
2011 FT_Face p_face = NULL;
2014 /* Look for a match amongst our attachments first */
2015 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2019 char *psz_fontfile = NULL;
2021 psz_fontfile = FontConfig_Select( NULL,
2022 p_style->psz_fontname,
2026 if( psz_fontfile && ! *psz_fontfile )
2028 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2029 " so using default font", p_style->psz_fontname,
2030 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2031 (p_style->b_bold ? "(Bold)" :
2032 (p_style->b_italic ? "(Italic)" : ""))) );
2033 free( psz_fontfile );
2034 psz_fontfile = NULL;
2039 if( FT_New_Face( p_sys->p_library,
2040 psz_fontfile, i_idx, &p_face ) )
2042 free( psz_fontfile );
2043 free( pp_char_styles );
2044 #if defined(HAVE_FRIBIDI)
2047 free( pi_karaoke_bar );
2048 return VLC_EGENERIC;
2050 free( psz_fontfile );
2054 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2056 /* We've loaded a font face which is unhelpful for actually
2057 * rendering text - fallback to the default one.
2059 FT_Done_Face( p_face );
2063 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2064 ft_encoding_unicode ) ||
2065 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2066 p_style->i_font_size ) )
2068 if( p_face ) FT_Done_Face( p_face );
2069 free( pp_char_styles );
2070 #if defined(HAVE_FRIBIDI)
2073 free( pi_karaoke_bar );
2074 return VLC_EGENERIC;
2076 p_sys->i_use_kerning =
2077 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2080 uint32_t *psz_unicode = (uint32_t *)
2081 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2084 if( p_face ) FT_Done_Face( p_face );
2085 free( pp_char_styles );
2086 free( psz_unicode );
2087 #if defined(HAVE_FRIBIDI)
2090 free( pi_karaoke_bar );
2093 memcpy( psz_unicode, psz_text + i_prev,
2094 sizeof( uint32_t ) * ( k - i_prev ) );
2095 psz_unicode[ k - i_prev ] = 0;
2096 while( *psz_unicode )
2100 if( !(p_line = NewLine( i_len - i_prev)) )
2102 if( p_face ) FT_Done_Face( p_face );
2103 free( pp_char_styles );
2104 free( psz_unicode );
2105 #if defined(HAVE_FRIBIDI)
2108 free( pi_karaoke_bar );
2111 /* New Color mode only works in YUVA rendering mode --
2112 * (RGB mode has palette constraints on it). We therefore
2113 * need to populate the legacy colour fields also.
2115 p_line->b_new_color_mode = true;
2116 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2117 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2118 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2119 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2120 p_line->p_next = NULL;
2122 i_pen_y += tmp_result.y;
2126 if( p_prev ) p_prev->p_next = p_line;
2127 else *pp_lines = p_line;
2130 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2131 p_style->i_font_color, p_style->b_underline,
2135 p_style->i_karaoke_bg_color,
2136 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2137 &tmp_result ) != VLC_SUCCESS )
2139 if( p_face ) FT_Done_Face( p_face );
2140 free( pp_char_styles );
2141 free( psz_unicode );
2142 #if defined(HAVE_FRIBIDI)
2145 free( pi_karaoke_bar );
2146 return VLC_EGENERIC;
2151 p_result->x = __MAX( p_result->x, tmp_result.x );
2152 p_result->y += tmp_result.y;
2157 if( *psz_unicode == '\n')
2161 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2163 *c_ptr = *(c_ptr+1);
2168 free( psz_unicode );
2169 if( p_face ) FT_Done_Face( p_face );
2173 free( pp_char_styles );
2174 #if defined(HAVE_FRIBIDI)
2179 p_result->x = __MAX( p_result->x, tmp_result.x );
2180 p_result->y += tmp_result.y;
2183 if( pi_karaoke_bar )
2186 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2188 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2190 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2194 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2196 /* 100% BG colour will render faster if we
2197 * instead make it 100% FG colour, so leave
2198 * the ratio alone and copy the value across
2200 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2204 if( pi_karaoke_bar[ i ] & 0x80 )
2206 /* Swap Left and Right sides over for Right aligned
2207 * language text (eg. Arabic, Hebrew)
2209 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2211 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2212 p_line->p_bg_rgb[ k ] = i_tmp;
2214 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2217 /* Jump over the '\n' at the line-end */
2220 free( pi_karaoke_bar );
2226 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2227 subpicture_region_t *p_region_in )
2229 int rv = VLC_SUCCESS;
2230 stream_t *p_sub = NULL;
2231 xml_reader_t *p_xml_reader = NULL;
2233 if( !p_region_in || !p_region_in->psz_html )
2234 return VLC_EGENERIC;
2236 /* Reset the default fontsize in case screen metrics have changed */
2237 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2239 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2240 (uint8_t *) p_region_in->psz_html,
2241 strlen( p_region_in->psz_html ),
2245 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2246 if( p_filter->p_sys->p_xml )
2248 bool b_karaoke = false;
2250 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2253 /* Look for Root Node */
2254 if( xml_ReaderRead( p_xml_reader ) == 1 )
2256 char *psz_node = xml_ReaderName( p_xml_reader );
2258 if( !strcasecmp( "karaoke", psz_node ) )
2260 /* We're going to have to render the text a number
2261 * of times to show the progress marker on the text.
2263 var_SetBool( p_filter, "text-rerender", true );
2266 else if( !strcasecmp( "text", psz_node ) )
2272 /* Only text and karaoke tags are supported */
2273 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2274 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2275 p_xml_reader = NULL;
2287 uint32_t i_runs = 0;
2288 uint32_t i_k_runs = 0;
2289 uint32_t *pi_run_lengths = NULL;
2290 uint32_t *pi_k_run_lengths = NULL;
2291 uint32_t *pi_k_durations = NULL;
2292 ft_style_t **pp_styles = NULL;
2294 line_desc_t *p_lines = NULL;
2296 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2297 sizeof( uint32_t ) );
2302 rv = ProcessNodes( p_filter, p_xml_reader,
2303 p_region_in->p_style, psz_text, &i_len,
2304 &i_runs, &pi_run_lengths, &pp_styles,
2306 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2309 p_region_out->i_x = p_region_in->i_x;
2310 p_region_out->i_y = p_region_in->i_y;
2312 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2314 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2315 pi_run_lengths, pp_styles, &p_lines, &result,
2316 b_karaoke, i_k_runs, pi_k_run_lengths,
2320 for( k=0; k<i_runs; k++)
2321 DeleteStyle( pp_styles[k] );
2323 free( pi_run_lengths );
2326 /* Don't attempt to render text that couldn't be layed out
2329 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2331 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2333 Render( p_filter, p_region_out, p_lines,
2334 result.x, result.y );
2338 RenderYUVA( p_filter, p_region_out, p_lines,
2339 result.x, result.y );
2343 FreeLines( p_lines );
2345 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2348 stream_Delete( p_sub );
2354 static char* FontConfig_Select( FcConfig* priv, const char* family,
2355 bool b_bold, bool b_italic, int *i_idx )
2358 FcPattern *pat, *p_pat;
2362 pat = FcPatternCreate();
2363 if (!pat) return NULL;
2365 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2366 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2367 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2368 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2370 FcDefaultSubstitute( pat );
2372 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2374 FcPatternDestroy( pat );
2378 p_pat = FcFontMatch( priv, pat, &result );
2379 FcPatternDestroy( pat );
2380 if( !p_pat ) return NULL;
2382 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2383 || ( val_b != FcTrue ) )
2385 FcPatternDestroy( p_pat );
2388 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2393 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2395 FcPatternDestroy( p_pat );
2400 if( strcasecmp((const char*)val_s, family ) != 0 )
2401 msg_Warn( p_filter, "fontconfig: selected font family is not"
2402 "the requested one: '%s' != '%s'\n",
2403 (const char*)val_s, family );
2406 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2408 FcPatternDestroy( p_pat );
2412 FcPatternDestroy( p_pat );
2413 return strdup( (const char*)val_s );
2417 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2418 uint32_t **psz_text_out, uint32_t *pi_runs,
2419 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2420 ft_style_t *p_style )
2422 VLC_UNUSED(p_filter);
2423 VLC_UNUSED(psz_text_in);
2424 VLC_UNUSED(psz_text_out);
2425 VLC_UNUSED(pi_runs);
2426 VLC_UNUSED(ppi_run_lengths);
2427 VLC_UNUSED(ppp_styles);
2428 VLC_UNUSED(p_style);
2431 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2432 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2433 bool b_uline, bool b_through )
2436 VLC_UNUSED(p_fonts);
2438 VLC_UNUSED(b_italic);
2439 VLC_UNUSED(b_uline);
2440 VLC_UNUSED(b_through);
2445 static void FreeLine( line_desc_t *p_line )
2448 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2450 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2452 free( p_line->pp_glyphs );
2453 free( p_line->p_glyph_pos );
2454 free( p_line->p_fg_rgb );
2455 free( p_line->p_bg_rgb );
2456 free( p_line->p_fg_bg_ratio );
2457 free( p_line->pi_underline_offset );
2458 free( p_line->pi_underline_thickness );
2462 static void FreeLines( line_desc_t *p_lines )
2464 line_desc_t *p_line, *p_next;
2466 if( !p_lines ) return;
2468 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2470 p_next = p_line->p_next;
2475 static line_desc_t *NewLine( int i_count )
2477 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2479 if( !p_line ) return NULL;
2480 p_line->i_height = 0;
2481 p_line->i_width = 0;
2482 p_line->p_next = NULL;
2484 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2485 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2486 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2487 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2488 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2489 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2490 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2491 if( ( p_line->pp_glyphs == NULL ) ||
2492 ( p_line->p_glyph_pos == NULL ) ||
2493 ( p_line->p_fg_rgb == NULL ) ||
2494 ( p_line->p_bg_rgb == NULL ) ||
2495 ( p_line->p_fg_bg_ratio == NULL ) ||
2496 ( p_line->pi_underline_offset == NULL ) ||
2497 ( p_line->pi_underline_thickness == NULL ) )
2499 free( p_line->pi_underline_thickness );
2500 free( p_line->pi_underline_offset );
2501 free( p_line->p_fg_rgb );
2502 free( p_line->p_bg_rgb );
2503 free( p_line->p_fg_bg_ratio );
2504 free( p_line->p_glyph_pos );
2505 free( p_line->pp_glyphs );
2509 p_line->pp_glyphs[0] = NULL;
2510 p_line->b_new_color_mode = false;
2515 static int GetFontSize( filter_t *p_filter )
2517 filter_sys_t *p_sys = p_filter->p_sys;
2521 if( p_sys->i_default_font_size )
2523 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2524 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2526 i_size = p_sys->i_default_font_size;
2530 var_Get( p_filter, "freetype-rel-fontsize", &val );
2533 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2534 p_filter->p_sys->i_display_height =
2535 p_filter->fmt_out.video.i_height;
2540 msg_Warn( p_filter, "invalid fontsize, using 12" );
2541 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2542 i_size = 12 * val.i_int / 1000;
2549 static int SetFontSize( filter_t *p_filter, int i_size )
2551 filter_sys_t *p_sys = p_filter->p_sys;
2555 i_size = GetFontSize( p_filter );
2557 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2560 p_sys->i_font_size = i_size;
2562 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2564 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2565 return VLC_EGENERIC;
2571 static void YUVFromRGB( uint32_t i_argb,
2572 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2574 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2575 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2576 int i_blue = ( i_argb & 0x000000ff );
2578 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2579 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2580 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2581 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2582 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2583 -585 * i_blue + 4096 + 1048576) >> 13, 240);