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>
47 #include FT_FREETYPE_H
49 #define FT_FLOOR(X) ((X & -64) >> 6)
50 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
51 #define FT_MulFix(v, s) (((v)*(s))>>16)
54 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
55 #define FC_DEFAULT_FONT "Lucida Grande"
56 #elif defined( SYS_BEOS )
57 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
58 #define FC_DEFAULT_FONT "Swiss"
59 #elif defined( WIN32 )
60 #define DEFAULT_FONT "" /* Default font found at run-time */
61 #define FC_DEFAULT_FONT "Arial"
63 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
64 #define FC_DEFAULT_FONT "Serif Bold"
67 #if defined(HAVE_FRIBIDI)
68 #include <fribidi/fribidi.h>
71 #ifdef HAVE_FONTCONFIG
72 #include <fontconfig/fontconfig.h>
74 #define DEFAULT_FONT FC_DEFAULT_FONT
79 /*****************************************************************************
81 *****************************************************************************/
82 static int Create ( vlc_object_t * );
83 static void Destroy( vlc_object_t * );
85 #define FONT_TEXT N_("Font")
87 #ifdef HAVE_FONTCONFIG
88 #define FONT_LONGTEXT N_("Font family for the font you want to use")
90 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
93 #define FONTSIZE_TEXT N_("Font size in pixels")
94 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
95 "that will be rendered on the video. " \
96 "If set to something different than 0 this option will override the " \
97 "relative font size." )
98 #define OPACITY_TEXT N_("Opacity")
99 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
100 "text that will be rendered on the video. 0 = transparent, " \
101 "255 = totally opaque. " )
102 #define COLOR_TEXT N_("Text default color")
103 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
104 "the video. This must be an hexadecimal (like HTML colors). The first two "\
105 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
106 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
107 #define FONTSIZER_TEXT N_("Relative font size")
108 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
109 "fonts that will be rendered on the video. If absolute font size is set, "\
110 "relative size will be overriden." )
112 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
113 static const char *const ppsz_sizes_text[] = {
114 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
115 #define YUVP_TEXT N_("Use YUVP renderer")
116 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
117 "This option is only needed if you want to encode into DVB subtitles" )
118 #define EFFECT_TEXT N_("Font Effect")
119 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
120 "text to improve its readability." )
122 #define EFFECT_BACKGROUND 1
123 #define EFFECT_OUTLINE 2
124 #define EFFECT_OUTLINE_FAT 3
126 static int const pi_effects[] = { 1, 2, 3 };
127 static const char *const ppsz_effects_text[] = {
128 N_("Background"),N_("Outline"), N_("Fat Outline") };
129 static const int pi_color_values[] = {
130 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
131 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
132 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
134 static const char *const ppsz_color_descriptions[] = {
135 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
136 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
137 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
140 set_shortname( N_("Text renderer"))
141 set_description( N_("Freetype2 font renderer") )
142 set_category( CAT_VIDEO )
143 set_subcategory( SUBCAT_VIDEO_SUBPIC )
145 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
148 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
149 FONTSIZE_LONGTEXT, true )
151 /* opacity valid on 0..255, with default 255 = fully opaque */
152 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
153 OPACITY_TEXT, OPACITY_LONGTEXT, true )
155 /* hook to the color values list, with default 0x00ffffff = white */
156 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
157 COLOR_LONGTEXT, false )
158 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
160 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
161 FONTSIZER_LONGTEXT, false )
162 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
163 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
164 EFFECT_LONGTEXT, false )
165 change_integer_list( pi_effects, ppsz_effects_text, NULL )
167 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
168 YUVP_LONGTEXT, true )
169 set_capability( "text renderer", 100 )
170 add_shortcut( "text" )
171 set_callbacks( Create, Destroy )
176 /*****************************************************************************
178 *****************************************************************************/
180 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
181 static int RenderText( filter_t *, subpicture_region_t *,
182 subpicture_region_t * );
183 #ifdef HAVE_FONTCONFIG
184 static int RenderHtml( filter_t *, subpicture_region_t *,
185 subpicture_region_t * );
186 static char *FontConfig_Select( FcConfig *, const char *,
191 static int LoadFontsFromAttachments( filter_t *p_filter );
193 static int GetFontSize( filter_t *p_filter );
194 static int SetFontSize( filter_t *, int );
195 static void YUVFromRGB( uint32_t i_argb,
196 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
198 typedef struct line_desc_t line_desc_t;
201 /** NULL-terminated list of glyphs making the string */
202 FT_BitmapGlyph *pp_glyphs;
203 /** list of relative positions for the glyphs */
204 FT_Vector *p_glyph_pos;
205 /** list of RGB information for styled text
206 * -- if the rendering mode supports it (RenderYUVA) and
207 * b_new_color_mode is set, then it becomes possible to
208 * have multicoloured text within the subtitles. */
211 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
212 bool b_new_color_mode;
213 /** underline information -- only supplied if text should be underlined */
214 uint16_t *pi_underline_offset;
215 uint16_t *pi_underline_thickness;
219 int i_red, i_green, i_blue;
224 static line_desc_t *NewLine( int );
229 uint32_t i_font_color; /* ARGB */
230 uint32_t i_karaoke_bg_color; /* ARGB */
237 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
238 static void FreeLines( line_desc_t * );
239 static void FreeLine( line_desc_t * );
241 #ifdef HAVE_FONTCONFIG
242 static vlc_object_t *FontBuilderAttach( filter_t *p_filter );
243 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
244 static void* FontBuilderThread( vlc_object_t *p_this);
245 static void FontBuilderDestructor( vlc_object_t *p_this );
246 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
247 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
251 /*****************************************************************************
252 * filter_sys_t: freetype local data
253 *****************************************************************************
254 * This structure is part of the video output thread descriptor.
255 * It describes the freetype specific properties of an output thread.
256 *****************************************************************************/
259 FT_Library p_library; /* handle to library */
260 FT_Face p_face; /* handle to face object */
262 uint8_t i_font_opacity;
267 int i_default_font_size;
268 int i_display_height;
269 #ifdef HAVE_FONTCONFIG
270 bool b_fontconfig_ok;
271 FcConfig *p_fontconfig;
274 input_attachment_t **pp_font_attachments;
275 int i_font_attachments;
277 vlc_object_t *p_fontbuilder;
280 #define UCHAR uint32_t
281 #define TR_DEFAULT_FONT FC_DEFAULT_FONT
282 #define TR_FONT_STYLE_PTR ft_style_t *
284 #include "text_renderer.h"
286 /*****************************************************************************
287 * Create: allocates osd-text video thread output method
288 *****************************************************************************
289 * This function allocates and initializes a Clone vout method.
290 *****************************************************************************/
291 static int Create( vlc_object_t *p_this )
293 filter_t *p_filter = (filter_t *)p_this;
295 char *psz_fontfile=NULL;
296 char *psz_fontfamily=NULL;
297 int i_error,fontindex;
299 #ifdef HAVE_FONTCONFIG
300 FcPattern *fontpattern = NULL, *fontmatch = NULL;
301 /* Initialise result to Match, as fontconfig doesnt
302 * really set this other than some error-cases */
303 FcResult fontresult = FcResultMatch;
307 /* Allocate structure */
308 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
312 p_sys->p_library = 0;
313 p_sys->i_font_size = 0;
314 p_sys->i_display_height = 0;
316 var_Create( p_filter, "freetype-rel-fontsize",
317 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
319 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
320 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
321 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
322 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
323 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
324 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
325 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
328 if( !psz_fontfamily || !*psz_fontfamily )
330 #ifdef HAVE_FONTCONFIG
331 free( psz_fontfamily);
332 psz_fontfamily=strdup( DEFAULT_FONT );
334 free( psz_fontfamily );
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 )
354 fontpattern = FcPatternCreate();
359 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
360 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
361 free( psz_fontsize );
363 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
365 FcDefaultSubstitute( fontpattern );
367 /* testing fontresult here doesn't do any good really, but maybe it will
368 * in future as fontconfig code doesn't set it in all cases and just
369 * returns NULL or doesn't set to to Match on all Match cases.*/
370 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
371 if( !fontmatch || fontresult == FcResultNoMatch )
374 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
375 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
378 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
380 psz_fontfile = psz_fontfamily;
383 i_error = FT_Init_FreeType( &p_sys->p_library );
386 msg_Err( p_filter, "couldn't initialize freetype" );
390 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
391 fontindex, &p_sys->p_face );
393 if( i_error == FT_Err_Unknown_File_Format )
395 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
400 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
404 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
407 msg_Err( p_filter, "font has no unicode translation table" );
411 #ifdef HAVE_FONTCONFIG
412 p_sys->b_fontconfig_ok = false;
413 p_sys->p_fontconfig = NULL;
414 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
417 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
419 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
422 p_sys->pp_font_attachments = NULL;
423 p_sys->i_font_attachments = 0;
425 p_filter->pf_render_text = RenderText;
426 #ifdef HAVE_FONTCONFIG
427 p_filter->pf_render_html = RenderHtml;
428 FcPatternDestroy( fontmatch );
429 FcPatternDestroy( fontpattern );
431 p_filter->pf_render_html = NULL;
434 free( psz_fontfamily );
435 LoadFontsFromAttachments( p_filter );
440 #ifdef HAVE_FONTCONFIG
441 if( fontmatch ) FcPatternDestroy( fontmatch );
442 if( fontpattern ) FcPatternDestroy( fontpattern );
444 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
445 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
446 free( psz_fontfamily );
451 /*****************************************************************************
452 * Destroy: destroy Clone video thread output method
453 *****************************************************************************
454 * Clean up all data and library connections
455 *****************************************************************************/
456 static void Destroy( vlc_object_t *p_this )
458 filter_t *p_filter = (filter_t *)p_this;
459 filter_sys_t *p_sys = p_filter->p_sys;
461 if( p_sys->pp_font_attachments )
465 for( k = 0; k < p_sys->i_font_attachments; k++ )
466 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
468 free( p_sys->pp_font_attachments );
471 #ifdef HAVE_FONTCONFIG
472 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
475 /* FcFini asserts calling the subfunction FcCacheFini()
476 * even if no other library functions have been made since FcInit(),
477 * so don't call it. */
479 FT_Done_Face( p_sys->p_face );
480 FT_Done_FreeType( p_sys->p_library );
484 #ifdef HAVE_FONTCONFIG
485 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
487 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
489 /* Check for an existing Fontbuilder thread */
490 vlc_mutex_lock( &fb_lock );
491 vlc_object_t *p_fontbuilder =
492 vlc_object_find_name( p_filter->p_libvlc,
493 "fontlist builder", FIND_CHILD );
497 /* Create the FontBuilderThread thread as a child of a top-level
498 * object, so that it can survive the destruction of the
499 * freetype object - the fontlist only needs to be built once,
500 * and calling the fontbuild a second time while the first is
501 * still in progress can cause thread instabilities.
503 * XXX The fontbuilder will be destroy as soon as it is unused.
506 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
507 sizeof(vlc_object_t) );
510 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
511 p_fontbuilder->p_private = NULL;
512 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
514 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
516 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
517 var_SetBool( p_fontbuilder, "build-done", false );
518 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
519 var_SetBool( p_fontbuilder, "build-joined", false );
521 if( vlc_thread_create( p_fontbuilder,
524 VLC_THREAD_PRIORITY_LOW ) )
526 msg_Warn( p_filter, "fontconfig database builder thread can't "
527 "be launched. Font styling support will be limited." );
534 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
535 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
537 vlc_mutex_unlock( &fb_lock );
538 return p_fontbuilder;
540 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
542 vlc_mutex_lock( &fb_lock );
545 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
547 /* We wait for the thread on the first FontBuilderDetach */
548 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
550 var_SetBool( p_fontbuilder, "build-joined", true );
551 vlc_mutex_unlock( &fb_lock );
552 /* We need to unlock otherwise we may not join (the thread waiting
553 * for the lock). It is safe to unlock as no one else will try a
554 * join and we have a reference on the object) */
555 vlc_thread_join( p_fontbuilder );
556 vlc_mutex_lock( &fb_lock );
558 vlc_object_release( p_fontbuilder );
560 vlc_mutex_unlock( &fb_lock );
562 static void* FontBuilderThread( vlc_object_t *p_this )
564 FcConfig *p_fontconfig = FcInitLoadConfig();
569 int canc = vlc_savecancel ();
571 //msg_Dbg( p_this, "Building font database..." );
572 msg_Dbg( p_this, "Building font database..." );
574 if(! FcConfigBuildFonts( p_fontconfig ))
576 /* Don't destroy the fontconfig object - we won't be able to do
577 * italics or bold or change the font face, but we will still
578 * be able to do underline and change the font size.
580 msg_Err( p_this, "fontconfig database can't be built. "
581 "Font styling won't be available" );
585 msg_Dbg( p_this, "Finished building font database." );
586 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
588 vlc_mutex_lock( &fb_lock );
589 p_this->p_private = p_fontconfig;
590 vlc_mutex_unlock( &fb_lock );
592 var_SetBool( p_this, "build-done", true );
593 vlc_restorecancel (canc);
597 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
599 filter_sys_t *p_sys = p_filter->p_sys;
601 p_sys->p_fontconfig = p_fontbuilder->p_private;
602 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
604 static void FontBuilderDestructor( vlc_object_t *p_this )
606 FcConfig *p_fontconfig = p_this->p_private;
609 FcConfigDestroy( p_fontconfig );
611 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
612 vlc_value_t oldval, vlc_value_t newval, void *param )
614 filter_t *p_filter = param;
618 vlc_mutex_lock( &fb_lock );
620 FontBuilderGetFcConfig( p_filter, p_this );
622 vlc_mutex_unlock( &fb_lock );
631 /*****************************************************************************
632 * Make any TTF/OTF fonts present in the attachments of the media file
633 * and store them for later use by the FreeType Engine
634 *****************************************************************************/
635 static int LoadFontsFromAttachments( filter_t *p_filter )
637 filter_sys_t *p_sys = p_filter->p_sys;
638 input_thread_t *p_input;
639 input_attachment_t **pp_attachments;
640 int i_attachments_cnt;
642 int rv = VLC_SUCCESS;
644 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
648 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
650 vlc_object_release(p_input);
654 p_sys->i_font_attachments = 0;
655 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
656 if(! p_sys->pp_font_attachments )
659 for( k = 0; k < i_attachments_cnt; k++ )
661 input_attachment_t *p_attach = pp_attachments[k];
663 if( p_sys->pp_font_attachments )
665 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
666 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
667 ( p_attach->i_data > 0 ) &&
668 ( p_attach->p_data != NULL ) )
670 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
674 vlc_input_attachment_Delete( p_attach );
679 vlc_input_attachment_Delete( p_attach );
682 free( pp_attachments );
684 vlc_object_release(p_input);
689 /*****************************************************************************
690 * Render: place string in picture
691 *****************************************************************************
692 * This function merges the previously rendered freetype glyphs into a picture
693 *****************************************************************************/
694 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
695 line_desc_t *p_line, int i_width, int i_height )
697 static const uint8_t pi_gamma[16] =
698 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
699 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
703 int i, x, y, i_pitch;
704 uint8_t i_y; /* YUV values, derived from incoming RGB */
707 /* Create a new subpicture region */
708 memset( &fmt, 0, sizeof(video_format_t) );
709 fmt.i_chroma = VLC_CODEC_YUVP;
711 fmt.i_width = fmt.i_visible_width = i_width + 4;
712 fmt.i_height = fmt.i_visible_height = i_height + 4;
713 if( p_region->fmt.i_visible_width > 0 )
714 fmt.i_visible_width = p_region->fmt.i_visible_width;
715 if( p_region->fmt.i_visible_height > 0 )
716 fmt.i_visible_height = p_region->fmt.i_visible_height;
717 fmt.i_x_offset = fmt.i_y_offset = 0;
719 assert( !p_region->p_picture );
720 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
721 if( !p_region->p_picture )
725 /* Calculate text color components */
726 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
727 25 * p_line->i_blue + 128) >> 8) + 16;
728 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
729 112 * p_line->i_blue + 128) >> 8) + 128;
730 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
731 18 * p_line->i_blue + 128) >> 8) + 128;
734 fmt.p_palette->i_entries = 16;
735 for( i = 0; i < 8; i++ )
737 fmt.p_palette->palette[i][0] = 0;
738 fmt.p_palette->palette[i][1] = 0x80;
739 fmt.p_palette->palette[i][2] = 0x80;
740 fmt.p_palette->palette[i][3] = pi_gamma[i];
741 fmt.p_palette->palette[i][3] =
742 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
744 for( i = 8; i < fmt.p_palette->i_entries; i++ )
746 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
747 fmt.p_palette->palette[i][1] = i_u;
748 fmt.p_palette->palette[i][2] = i_v;
749 fmt.p_palette->palette[i][3] = pi_gamma[i];
750 fmt.p_palette->palette[i][3] =
751 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
754 p_dst = p_region->p_picture->Y_PIXELS;
755 i_pitch = p_region->p_picture->Y_PITCH;
757 /* Initialize the region pixels */
758 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
760 for( ; p_line != NULL; p_line = p_line->p_next )
762 int i_glyph_tmax = 0;
763 int i_bitmap_offset, i_offset, i_align_offset = 0;
764 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
766 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
767 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
770 if( p_line->i_width < i_width )
772 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
774 i_align_offset = i_width - p_line->i_width;
776 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
778 i_align_offset = ( i_width - p_line->i_width ) / 2;
782 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
784 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
786 i_offset = ( p_line->p_glyph_pos[ i ].y +
787 i_glyph_tmax - p_glyph->top + 2 ) *
788 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
791 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
793 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
795 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
797 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
804 /* Outlining (find something better than nearest neighbour filtering ?) */
807 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
808 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
809 uint8_t left, current;
811 for( y = 1; y < (int)fmt.i_height - 1; y++ )
813 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
814 p_dst += p_region->p_picture->Y_PITCH;
817 for( x = 1; x < (int)fmt.i_width - 1; x++ )
820 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
821 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;
825 memset( p_top, 0, fmt.i_width );
831 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
832 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
833 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
834 int i_glyph_tmax, int i_align_offset,
835 uint8_t i_y, uint8_t i_u, uint8_t i_v,
836 subpicture_region_t *p_region)
840 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
842 p_dst_y = p_region->p_picture->Y_PIXELS;
843 p_dst_u = p_region->p_picture->U_PIXELS;
844 p_dst_v = p_region->p_picture->V_PIXELS;
845 p_dst_a = p_region->p_picture->A_PIXELS;
846 i_pitch = p_region->p_picture->A_PITCH;
848 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
849 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
851 for( y = 0; y < i_line_thickness; y++ )
853 int i_extra = p_this_glyph->bitmap.width;
857 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
858 (p_this_glyph_pos->x + p_this_glyph->left);
860 for( x = 0; x < i_extra; x++ )
864 /* break the underline around the tails of any glyphs which cross it */
865 for( z = x - i_line_thickness;
866 z < x + i_line_thickness && b_ok;
869 if( p_next_glyph && ( z >= i_extra ) )
871 int i_row = i_line_offset + p_next_glyph->top + y;
873 if( ( p_next_glyph->bitmap.rows > i_row ) &&
874 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
879 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
881 int i_row = i_line_offset + p_this_glyph->top + y;
883 if( ( p_this_glyph->bitmap.rows > i_row ) &&
884 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
893 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
894 p_dst_u[i_offset+x] = i_u;
895 p_dst_v[i_offset+x] = i_v;
896 p_dst_a[i_offset+x] = 255;
903 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
905 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
906 int i_pitch = p_region->p_picture->A_PITCH;
909 for( ; p_line != NULL; p_line = p_line->p_next )
911 int i_glyph_tmax=0, i = 0;
912 int i_bitmap_offset, i_offset, i_align_offset = 0;
913 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
915 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
916 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
919 if( p_line->i_width < i_width )
921 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
923 i_align_offset = i_width - p_line->i_width;
925 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
927 i_align_offset = ( i_width - p_line->i_width ) / 2;
931 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
933 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
935 i_offset = ( p_line->p_glyph_pos[ i ].y +
936 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
937 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
938 i_align_offset +xoffset;
940 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
942 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
944 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
945 if( p_dst[i_offset+x] <
946 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
948 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
957 /*****************************************************************************
958 * Render: place string in picture
959 *****************************************************************************
960 * This function merges the previously rendered freetype glyphs into a picture
961 *****************************************************************************/
962 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
963 line_desc_t *p_line, int i_width, int i_height )
965 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
967 int i, x, y, i_pitch, i_alpha;
968 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
970 if( i_width == 0 || i_height == 0 )
973 /* Create a new subpicture region */
974 memset( &fmt, 0, sizeof(video_format_t) );
975 fmt.i_chroma = VLC_CODEC_YUVA;
977 fmt.i_width = fmt.i_visible_width = i_width + 6;
978 fmt.i_height = fmt.i_visible_height = i_height + 6;
979 if( p_region->fmt.i_visible_width > 0 )
980 fmt.i_visible_width = p_region->fmt.i_visible_width;
981 if( p_region->fmt.i_visible_height > 0 )
982 fmt.i_visible_height = p_region->fmt.i_visible_height;
983 fmt.i_x_offset = fmt.i_y_offset = 0;
985 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
986 if( !p_region->p_picture )
990 /* Calculate text color components */
991 YUVFromRGB( (p_line->i_red << 16) |
992 (p_line->i_green << 8) |
995 i_alpha = p_line->i_alpha;
997 p_dst_y = p_region->p_picture->Y_PIXELS;
998 p_dst_u = p_region->p_picture->U_PIXELS;
999 p_dst_v = p_region->p_picture->V_PIXELS;
1000 p_dst_a = p_region->p_picture->A_PIXELS;
1001 i_pitch = p_region->p_picture->A_PITCH;
1003 /* Initialize the region pixels */
1004 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
1006 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
1007 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1008 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1009 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1013 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1014 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1015 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1016 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1018 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1019 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1021 DrawBlack( p_line, i_width, p_region, 0, 0);
1022 DrawBlack( p_line, i_width, p_region, -1, 0);
1023 DrawBlack( p_line, i_width, p_region, 0, -1);
1024 DrawBlack( p_line, i_width, p_region, 1, 0);
1025 DrawBlack( p_line, i_width, p_region, 0, 1);
1028 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1030 DrawBlack( p_line, i_width, p_region, -1, -1);
1031 DrawBlack( p_line, i_width, p_region, -1, 1);
1032 DrawBlack( p_line, i_width, p_region, 1, -1);
1033 DrawBlack( p_line, i_width, p_region, 1, 1);
1035 DrawBlack( p_line, i_width, p_region, -2, 0);
1036 DrawBlack( p_line, i_width, p_region, 0, -2);
1037 DrawBlack( p_line, i_width, p_region, 2, 0);
1038 DrawBlack( p_line, i_width, p_region, 0, 2);
1040 DrawBlack( p_line, i_width, p_region, -2, -2);
1041 DrawBlack( p_line, i_width, p_region, -2, 2);
1042 DrawBlack( p_line, i_width, p_region, 2, -2);
1043 DrawBlack( p_line, i_width, p_region, 2, 2);
1045 DrawBlack( p_line, i_width, p_region, -3, 0);
1046 DrawBlack( p_line, i_width, p_region, 0, -3);
1047 DrawBlack( p_line, i_width, p_region, 3, 0);
1048 DrawBlack( p_line, i_width, p_region, 0, 3);
1051 for( ; p_line != NULL; p_line = p_line->p_next )
1053 int i_glyph_tmax = 0;
1054 int i_bitmap_offset, i_offset, i_align_offset = 0;
1055 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1057 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1058 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1061 if( p_line->i_width < i_width )
1063 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1065 i_align_offset = i_width - p_line->i_width;
1067 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1069 i_align_offset = ( i_width - p_line->i_width ) / 2;
1073 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1075 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1077 i_offset = ( p_line->p_glyph_pos[ i ].y +
1078 i_glyph_tmax - p_glyph->top + 3 ) *
1079 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1082 if( p_line->b_new_color_mode )
1084 /* Every glyph can (and in fact must) have its own color */
1085 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1088 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1090 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1092 uint8_t i_y_local = i_y;
1093 uint8_t i_u_local = i_u;
1094 uint8_t i_v_local = i_v;
1096 if( p_line->p_fg_bg_ratio != 0x00 )
1098 int i_split = p_glyph->bitmap.width *
1099 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1103 YUVFromRGB( p_line->p_bg_rgb[ i ],
1104 &i_y_local, &i_u_local, &i_v_local );
1108 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1110 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1111 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1113 p_dst_u[i_offset+x] = i_u;
1114 p_dst_v[i_offset+x] = i_v;
1116 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1117 p_dst_a[i_offset+x] = 0xff;
1120 i_offset += i_pitch;
1123 if( p_line->pi_underline_thickness[ i ] )
1125 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1126 p_line->pi_underline_offset[ i ],
1127 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1128 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1129 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1130 i_glyph_tmax, i_align_offset,
1137 /* Apply the alpha setting */
1138 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1139 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1145 * This function renders a text subpicture region into another one.
1146 * It also calculates the size needed for this string, and renders the
1147 * needed glyphs into memory. It is used as pf_add_string callback in
1148 * the vout method by this module
1150 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1151 subpicture_region_t *p_region_in )
1153 filter_sys_t *p_sys = p_filter->p_sys;
1154 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1155 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1156 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1157 int i_string_length;
1159 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1160 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1170 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1171 psz_string = p_region_in->psz_text;
1172 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1174 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1175 i_scale = val.i_int;
1177 if( p_region_in->p_style )
1179 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1180 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1181 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1185 i_font_color = p_sys->i_font_color;
1186 i_font_alpha = 255 - p_sys->i_font_opacity;
1187 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1190 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1191 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1192 SetFontSize( p_filter, i_font_size );
1194 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1195 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1196 i_blue = i_font_color & 0x000000FF;
1198 result.x = result.y = 0;
1199 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1201 psz_unicode = psz_unicode_orig =
1202 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1203 if( psz_unicode == NULL )
1205 #if defined(WORDS_BIGENDIAN)
1206 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1208 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1210 if( iconv_handle == (vlc_iconv_t)-1 )
1212 msg_Warn( p_filter, "unable to do conversion" );
1218 const char *p_in_buffer = psz_string;
1219 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1220 i_in_bytes = strlen( psz_string );
1221 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1222 i_out_bytes_left = i_out_bytes;
1223 p_out_buffer = (char *)psz_unicode;
1224 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1226 &p_out_buffer, &i_out_bytes_left );
1228 vlc_iconv_close( iconv_handle );
1232 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1233 "bytes left %u", (unsigned)i_in_bytes );
1236 *(uint32_t*)p_out_buffer = 0;
1237 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1240 #if defined(HAVE_FRIBIDI)
1242 uint32_t *p_fribidi_string;
1243 int32_t start_pos, pos = 0;
1245 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1246 if( !p_fribidi_string )
1249 /* Do bidi conversion line-by-line */
1250 while( pos < i_string_length )
1252 while( pos < i_string_length )
1254 i_char = psz_unicode[pos];
1255 if (i_char != '\r' && i_char != '\n')
1257 p_fribidi_string[pos] = i_char;
1261 while( pos < i_string_length )
1263 i_char = psz_unicode[pos];
1264 if (i_char == '\r' || i_char == '\n')
1268 if (pos > start_pos)
1270 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1271 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1274 (FriBidiChar*)p_fribidi_string + start_pos,
1279 free( psz_unicode_orig );
1280 psz_unicode = psz_unicode_orig = p_fribidi_string;
1281 p_fribidi_string[ i_string_length ] = 0;
1285 /* Calculate relative glyph positions and a bounding box for the
1287 if( !(p_line = NewLine( strlen( psz_string ))) )
1290 i_pen_x = i_pen_y = 0;
1292 psz_line_start = psz_unicode;
1294 #define face p_sys->p_face
1295 #define glyph face->glyph
1297 while( *psz_unicode )
1299 i_char = *psz_unicode++;
1300 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1305 if( i_char == '\n' )
1307 psz_line_start = psz_unicode;
1308 if( !(p_next = NewLine( strlen( psz_string ))) )
1310 p_line->p_next = p_next;
1311 p_line->i_width = line.xMax;
1312 p_line->i_height = face->size->metrics.height >> 6;
1313 p_line->pp_glyphs[ i ] = NULL;
1314 p_line->i_alpha = i_font_alpha;
1315 p_line->i_red = i_red;
1316 p_line->i_green = i_green;
1317 p_line->i_blue = i_blue;
1320 result.x = __MAX( result.x, line.xMax );
1321 result.y += face->size->metrics.height >> 6;
1324 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1325 i_pen_y += face->size->metrics.height >> 6;
1327 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1332 i_glyph_index = FT_Get_Char_Index( face, i_char );
1333 if( p_sys->i_use_kerning && i_glyph_index
1337 FT_Get_Kerning( face, i_previous, i_glyph_index,
1338 ft_kerning_default, &delta );
1339 i_pen_x += delta.x >> 6;
1342 p_line->p_glyph_pos[ i ].x = i_pen_x;
1343 p_line->p_glyph_pos[ i ].y = i_pen_y;
1344 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1347 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1351 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1354 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1358 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1359 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1362 FT_Done_Glyph( tmp_glyph );
1365 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1368 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1369 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1370 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1372 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1373 p_line->pp_glyphs[ i ] = NULL;
1375 p_line = NewLine( strlen( psz_string ));
1376 if( p_prev ) p_prev->p_next = p_line;
1377 else p_lines = p_line;
1379 uint32_t *psz_unicode_saved = psz_unicode;
1380 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1384 if( psz_unicode == psz_line_start )
1385 { /* try harder to break that line */
1386 psz_unicode = psz_unicode_saved;
1387 while( psz_unicode > psz_line_start &&
1388 *psz_unicode != '_' && *psz_unicode != '/' &&
1389 *psz_unicode != '\\' && *psz_unicode != '.' )
1394 if( psz_unicode == psz_line_start )
1396 msg_Warn( p_filter, "unbreakable string" );
1401 *psz_unicode = '\n';
1403 psz_unicode = psz_line_start;
1406 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1409 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1410 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1412 i_previous = i_glyph_index;
1413 i_pen_x += glyph->advance.x >> 6;
1417 p_line->i_width = line.xMax;
1418 p_line->i_height = face->size->metrics.height >> 6;
1419 p_line->pp_glyphs[ i ] = NULL;
1420 p_line->i_alpha = i_font_alpha;
1421 p_line->i_red = i_red;
1422 p_line->i_green = i_green;
1423 p_line->i_blue = i_blue;
1424 result.x = __MAX( result.x, line.xMax );
1425 result.y += line.yMax - line.yMin;
1430 p_region_out->i_x = p_region_in->i_x;
1431 p_region_out->i_y = p_region_in->i_y;
1433 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1434 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1436 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1438 free( psz_unicode_orig );
1439 FreeLines( p_lines );
1443 free( psz_unicode_orig );
1444 FreeLines( p_lines );
1445 return VLC_EGENERIC;
1448 #ifdef HAVE_FONTCONFIG
1449 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1450 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1451 bool b_italic, bool b_uline )
1453 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1457 p_style->i_font_size = i_font_size;
1458 p_style->i_font_color = i_font_color;
1459 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1460 p_style->b_italic = b_italic;
1461 p_style->b_bold = b_bold;
1462 p_style->b_underline = b_uline;
1464 p_style->psz_fontname = strdup( psz_fontname );
1469 static void DeleteStyle( ft_style_t *p_style )
1473 free( p_style->psz_fontname );
1478 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1485 if(( s1->i_font_size == s2->i_font_size ) &&
1486 ( s1->i_font_color == s2->i_font_color ) &&
1487 ( s1->b_italic == s2->b_italic ) &&
1488 ( s1->b_bold == s2->b_bold ) &&
1489 ( s1->b_underline == s2->b_underline ) &&
1490 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1497 static void IconvText( filter_t *p_filter, const char *psz_string,
1498 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1500 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1502 /* If memory hasn't been allocated for our output string, allocate it here
1503 * - the calling function must now be responsible for freeing it.
1505 if( !*ppsz_unicode )
1506 *ppsz_unicode = (uint32_t *)
1507 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1509 /* We don't need to handle a NULL pointer in *ppsz_unicode
1510 * if we are instead testing for a non NULL value like we are here */
1514 #if defined(WORDS_BIGENDIAN)
1515 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1517 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1519 if( iconv_handle != (vlc_iconv_t)-1 )
1521 char *p_in_buffer, *p_out_buffer;
1522 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1523 i_in_bytes = strlen( psz_string );
1524 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1525 i_out_bytes_left = i_out_bytes;
1526 p_in_buffer = (char *) psz_string;
1527 p_out_buffer = (char *) *ppsz_unicode;
1528 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1529 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1531 vlc_iconv_close( iconv_handle );
1535 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1536 "bytes left %u", (unsigned)i_in_bytes );
1540 *(uint32_t*)p_out_buffer = 0;
1542 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1547 msg_Warn( p_filter, "unable to do conversion" );
1552 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1553 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1556 ft_style_t *p_style = NULL;
1558 char *psz_fontname = NULL;
1559 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1560 uint32_t i_karaoke_bg_color = i_font_color;
1561 int i_font_size = p_sys->i_font_size;
1563 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1564 &i_font_color, &i_karaoke_bg_color ))
1566 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1567 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1572 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1573 bool b_uline, int i_karaoke_bgcolor,
1574 line_desc_t *p_line, uint32_t *psz_unicode,
1575 int *pi_pen_x, int i_pen_y, int *pi_start,
1576 FT_Vector *p_result )
1581 bool b_first_on_line = true;
1584 int i_pen_x_start = *pi_pen_x;
1586 uint32_t *psz_unicode_start = psz_unicode;
1588 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1590 /* Account for part of line already in position */
1591 for( i=0; i<*pi_start; i++ )
1595 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1596 ft_glyph_bbox_pixels, &glyph_size );
1598 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1599 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1600 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1601 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1607 b_first_on_line = false;
1609 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1615 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1616 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1620 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1621 ft_kerning_default, &delta );
1622 *pi_pen_x += delta.x >> 6;
1624 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1625 p_line->p_glyph_pos[ i ].y = i_pen_y;
1627 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1631 "unable to render text FT_Load_Glyph returned %d", i_error );
1632 p_line->pp_glyphs[ i ] = NULL;
1633 return VLC_EGENERIC;
1635 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1639 "unable to render text FT_Get_Glyph returned %d", i_error );
1640 p_line->pp_glyphs[ i ] = NULL;
1641 return VLC_EGENERIC;
1643 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1644 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1647 FT_Done_Glyph( tmp_glyph );
1652 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1653 p_face->size->metrics.y_scale));
1654 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1655 p_face->size->metrics.y_scale));
1657 p_line->pi_underline_offset[ i ] =
1658 ( aOffset < 0 ) ? -aOffset : aOffset;
1659 p_line->pi_underline_thickness[ i ] =
1660 ( aSize < 0 ) ? -aSize : aSize;
1662 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1663 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1664 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1665 p_line->p_fg_bg_ratio[ i ] = 0x00;
1667 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1668 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1669 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1671 for( ; i >= *pi_start; i-- )
1672 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1675 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1679 if( psz_unicode == psz_unicode_start )
1681 if( b_first_on_line )
1683 msg_Warn( p_filter, "unbreakable string" );
1684 p_line->pp_glyphs[ i ] = NULL;
1685 return VLC_EGENERIC;
1687 *pi_pen_x = i_pen_x_start;
1689 p_line->i_width = line.xMax;
1690 p_line->i_height = __MAX( p_line->i_height,
1691 p_face->size->metrics.height >> 6 );
1692 p_line->pp_glyphs[ i ] = NULL;
1694 p_result->x = __MAX( p_result->x, line.xMax );
1695 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1696 i_yMax - i_yMin ) );
1701 *psz_unicode = '\n';
1703 psz_unicode = psz_unicode_start;
1704 *pi_pen_x = i_pen_x_start;
1712 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1713 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1715 i_previous = i_glyph_index;
1716 *pi_pen_x += p_face->glyph->advance.x >> 6;
1719 p_line->i_width = line.xMax;
1720 p_line->i_height = __MAX( p_line->i_height,
1721 p_face->size->metrics.height >> 6 );
1722 p_line->pp_glyphs[ i ] = NULL;
1724 p_result->x = __MAX( p_result->x, line.xMax );
1725 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1726 line.yMax - line.yMin ) );
1730 /* Get rid of any text processed - if necessary repositioning
1731 * at the start of a new line of text
1735 *psz_unicode_start = '\0';
1737 else if( psz_unicode > psz_unicode_start )
1739 for( i=0; psz_unicode[ i ]; i++ )
1740 psz_unicode_start[ i ] = psz_unicode[ i ];
1741 psz_unicode_start[ i ] = '\0';
1747 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1748 uint32_t **psz_text_out, uint32_t *pi_runs,
1749 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1750 ft_style_t *p_style )
1752 uint32_t i_string_length = 0;
1754 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1755 *psz_text_out += i_string_length;
1757 if( ppp_styles && ppi_run_lengths )
1763 *ppp_styles = (ft_style_t **)
1764 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1766 else if( *pi_runs == 1 )
1768 *ppp_styles = (ft_style_t **)
1769 malloc( *pi_runs * sizeof( ft_style_t * ) );
1772 /* We have just malloc'ed this memory successfully -
1773 * *pi_runs HAS to be within the memory area of *ppp_styles */
1776 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1780 if( *ppi_run_lengths )
1782 *ppi_run_lengths = (uint32_t *)
1783 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1785 else if( *pi_runs == 1 )
1787 *ppi_run_lengths = (uint32_t *)
1788 malloc( *pi_runs * sizeof( uint32_t ) );
1791 /* same remarks here */
1792 if( *ppi_run_lengths )
1794 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1797 /* If we couldn't use the p_style argument due to memory allocation
1798 * problems above, release it here.
1800 if( p_style ) DeleteStyle( p_style );
1803 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1807 for( k=0; k < p_sys->i_font_attachments; k++ )
1809 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1811 FT_Face p_face = NULL;
1813 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1821 bool match = !strcasecmp( p_face->family_name,
1822 p_style->psz_fontname );
1824 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1825 match = match && p_style->b_bold;
1827 match = match && !p_style->b_bold;
1829 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1830 match = match && p_style->b_italic;
1832 match = match && !p_style->b_italic;
1840 FT_Done_Face( p_face );
1845 return VLC_EGENERIC;
1848 static int ProcessLines( filter_t *p_filter,
1853 uint32_t *pi_run_lengths,
1854 ft_style_t **pp_styles,
1855 line_desc_t **pp_lines,
1857 FT_Vector *p_result,
1861 uint32_t *pi_k_run_lengths,
1862 uint32_t *pi_k_durations )
1864 filter_sys_t *p_sys = p_filter->p_sys;
1865 ft_style_t **pp_char_styles;
1866 int *p_new_positions = NULL;
1867 int8_t *p_levels = NULL;
1868 uint8_t *pi_karaoke_bar = NULL;
1872 /* Assign each character in the text string its style explicitly, so that
1873 * after the characters have been shuffled around by Fribidi, we can re-apply
1874 * the styles, and to simplify the calculation of runs within a line.
1876 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1877 if( !pp_char_styles )
1882 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1883 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1884 * we just won't be able to display the progress bar; at least we'll
1890 for( j = 0; j < i_runs; j++ )
1891 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1892 pp_char_styles[ i++ ] = pp_styles[ j ];
1894 #if defined(HAVE_FRIBIDI)
1896 ft_style_t **pp_char_styles_new;
1897 int *p_old_positions;
1898 uint32_t *p_fribidi_string;
1899 int start_pos, pos = 0;
1901 pp_char_styles_new = (ft_style_t **)
1902 malloc( i_len * sizeof( ft_style_t * ));
1904 p_fribidi_string = (uint32_t *)
1905 malloc( (i_len + 1) * sizeof(uint32_t) );
1906 p_old_positions = (int *)
1907 malloc( (i_len + 1) * sizeof( int ) );
1908 p_new_positions = (int *)
1909 malloc( (i_len + 1) * sizeof( int ) );
1910 p_levels = (int8_t *)
1911 malloc( (i_len + 1) * sizeof( int8_t ) );
1913 if( ! pp_char_styles_new ||
1914 ! p_fribidi_string ||
1915 ! p_old_positions ||
1916 ! p_new_positions ||
1920 free( p_old_positions );
1921 free( p_new_positions );
1922 free( p_fribidi_string );
1923 free( pp_char_styles_new );
1924 free( pi_karaoke_bar );
1926 free( pp_char_styles );
1930 /* Do bidi conversion line-by-line */
1933 while(pos < i_len) {
1934 if (psz_text[pos] != '\n')
1936 p_fribidi_string[pos] = psz_text[pos];
1937 pp_char_styles_new[pos] = pp_char_styles[pos];
1938 p_new_positions[pos] = pos;
1943 while(pos < i_len) {
1944 if (psz_text[pos] == '\n')
1948 if (pos > start_pos)
1950 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1951 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1952 pos - start_pos, &base_dir,
1953 (FriBidiChar*)p_fribidi_string + start_pos,
1954 p_new_positions + start_pos,
1956 p_levels + start_pos );
1957 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1959 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1960 p_old_positions[ j - start_pos ] ];
1961 p_new_positions[ j ] += start_pos;
1965 free( p_old_positions );
1966 free( pp_char_styles );
1967 pp_char_styles = pp_char_styles_new;
1968 psz_text = p_fribidi_string;
1969 p_fribidi_string[ i_len ] = 0;
1972 /* Work out the karaoke */
1973 if( pi_karaoke_bar )
1975 int64_t i_last_duration = 0;
1976 int64_t i_duration = 0;
1977 int64_t i_start_pos = 0;
1978 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1980 for( k = 0; k< i_k_runs; k++ )
1982 double fraction = 0.0;
1984 i_duration += pi_k_durations[ k ];
1986 if( i_duration < i_elapsed )
1988 /* Completely finished this run-length -
1989 * let it render normally */
1993 else if( i_elapsed < i_last_duration )
1995 /* Haven't got up to this segment yet -
1996 * render it completely in karaoke BG mode */
2002 /* Partway through this run */
2004 fraction = (double)(i_elapsed - i_last_duration) /
2005 (double)pi_k_durations[ k ];
2007 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2009 double shade = pi_k_run_lengths[ k ] * fraction;
2011 if( p_new_positions )
2012 j = p_new_positions[ i_start_pos + i ];
2014 j = i_start_pos + i;
2016 if( i < (uint32_t)shade )
2017 pi_karaoke_bar[ j ] = 0xff;
2018 else if( (double)i > shade )
2019 pi_karaoke_bar[ j ] = 0x00;
2022 shade -= (int)shade;
2023 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2024 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2028 i_last_duration = i_duration;
2029 i_start_pos += pi_k_run_lengths[ k ];
2033 free( p_new_positions );
2035 FT_Vector tmp_result;
2037 line_desc_t *p_line = NULL;
2038 line_desc_t *p_prev = NULL;
2044 p_result->x = p_result->y = 0;
2045 tmp_result.x = tmp_result.y = 0;
2048 for( k = 0; k <= (uint32_t) i_len; k++ )
2050 if( ( k == (uint32_t) i_len ) ||
2052 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2054 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2056 /* End of the current style run */
2057 FT_Face p_face = NULL;
2060 /* Look for a match amongst our attachments first */
2061 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2065 char *psz_fontfile = NULL;
2067 vlc_mutex_lock( &fb_lock );
2068 if( p_sys->b_fontconfig_ok )
2070 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2071 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2072 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2073 p_style->psz_fontname,
2078 vlc_mutex_unlock( &fb_lock );
2080 if( psz_fontfile && ! *psz_fontfile )
2082 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2083 " so using default font", p_style->psz_fontname,
2084 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2085 (p_style->b_bold ? "(Bold)" :
2086 (p_style->b_italic ? "(Italic)" : ""))) );
2087 free( psz_fontfile );
2088 psz_fontfile = NULL;
2093 if( FT_New_Face( p_sys->p_library,
2094 psz_fontfile, i_idx, &p_face ) )
2096 free( psz_fontfile );
2097 free( pp_char_styles );
2098 #if defined(HAVE_FRIBIDI)
2101 free( pi_karaoke_bar );
2102 return VLC_EGENERIC;
2104 free( psz_fontfile );
2108 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2110 /* We've loaded a font face which is unhelpful for actually
2111 * rendering text - fallback to the default one.
2113 FT_Done_Face( p_face );
2117 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2118 ft_encoding_unicode ) ||
2119 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2120 p_style->i_font_size ) )
2122 if( p_face ) FT_Done_Face( p_face );
2123 free( pp_char_styles );
2124 #if defined(HAVE_FRIBIDI)
2127 free( pi_karaoke_bar );
2128 return VLC_EGENERIC;
2130 p_sys->i_use_kerning =
2131 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2134 uint32_t *psz_unicode = (uint32_t *)
2135 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2138 if( p_face ) FT_Done_Face( p_face );
2139 free( pp_char_styles );
2140 free( psz_unicode );
2141 #if defined(HAVE_FRIBIDI)
2144 free( pi_karaoke_bar );
2147 memcpy( psz_unicode, psz_text + i_prev,
2148 sizeof( uint32_t ) * ( k - i_prev ) );
2149 psz_unicode[ k - i_prev ] = 0;
2150 while( *psz_unicode )
2154 if( !(p_line = NewLine( i_len - i_prev)) )
2156 if( p_face ) FT_Done_Face( p_face );
2157 free( pp_char_styles );
2158 free( psz_unicode );
2159 #if defined(HAVE_FRIBIDI)
2162 free( pi_karaoke_bar );
2165 /* New Color mode only works in YUVA rendering mode --
2166 * (RGB mode has palette constraints on it). We therefore
2167 * need to populate the legacy colour fields also.
2169 p_line->b_new_color_mode = true;
2170 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2171 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2172 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2173 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2174 p_line->p_next = NULL;
2176 i_pen_y += tmp_result.y;
2180 if( p_prev ) p_prev->p_next = p_line;
2181 else *pp_lines = p_line;
2184 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2185 p_style->i_font_color, p_style->b_underline,
2186 p_style->i_karaoke_bg_color,
2187 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2188 &tmp_result ) != VLC_SUCCESS )
2190 if( p_face ) FT_Done_Face( p_face );
2191 free( pp_char_styles );
2192 free( psz_unicode );
2193 #if defined(HAVE_FRIBIDI)
2196 free( pi_karaoke_bar );
2197 return VLC_EGENERIC;
2202 p_result->x = __MAX( p_result->x, tmp_result.x );
2203 p_result->y += tmp_result.y;
2208 if( *psz_unicode == '\n')
2212 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2214 *c_ptr = *(c_ptr+1);
2219 free( psz_unicode );
2220 if( p_face ) FT_Done_Face( p_face );
2224 free( pp_char_styles );
2225 #if defined(HAVE_FRIBIDI)
2230 p_result->x = __MAX( p_result->x, tmp_result.x );
2231 p_result->y += tmp_result.y;
2234 if( pi_karaoke_bar )
2237 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2239 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2241 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2245 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2247 /* 100% BG colour will render faster if we
2248 * instead make it 100% FG colour, so leave
2249 * the ratio alone and copy the value across
2251 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2255 if( pi_karaoke_bar[ i ] & 0x80 )
2257 /* Swap Left and Right sides over for Right aligned
2258 * language text (eg. Arabic, Hebrew)
2260 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2262 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2263 p_line->p_bg_rgb[ k ] = i_tmp;
2265 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2268 /* Jump over the '\n' at the line-end */
2271 free( pi_karaoke_bar );
2277 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2278 subpicture_region_t *p_region_in )
2280 int rv = VLC_SUCCESS;
2281 stream_t *p_sub = NULL;
2282 xml_t *p_xml = NULL;
2283 xml_reader_t *p_xml_reader = NULL;
2285 if( !p_region_in || !p_region_in->psz_html )
2286 return VLC_EGENERIC;
2288 /* Reset the default fontsize in case screen metrics have changed */
2289 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2291 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2292 (uint8_t *) p_region_in->psz_html,
2293 strlen( p_region_in->psz_html ),
2297 p_xml = xml_Create( p_filter );
2300 bool b_karaoke = false;
2302 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2305 /* Look for Root Node */
2306 if( xml_ReaderRead( p_xml_reader ) == 1 )
2308 char *psz_node = xml_ReaderName( p_xml_reader );
2310 if( !strcasecmp( "karaoke", psz_node ) )
2312 /* We're going to have to render the text a number
2313 * of times to show the progress marker on the text.
2315 var_SetBool( p_filter, "text-rerender", true );
2318 else if( !strcasecmp( "text", psz_node ) )
2324 /* Only text and karaoke tags are supported */
2325 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2326 xml_ReaderDelete( p_xml, p_xml_reader );
2327 p_xml_reader = NULL;
2339 uint32_t i_runs = 0;
2340 uint32_t i_k_runs = 0;
2341 uint32_t *pi_run_lengths = NULL;
2342 uint32_t *pi_k_run_lengths = NULL;
2343 uint32_t *pi_k_durations = NULL;
2344 ft_style_t **pp_styles = NULL;
2346 line_desc_t *p_lines = NULL;
2348 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2349 sizeof( uint32_t ) );
2354 rv = ProcessNodes( p_filter, p_xml_reader,
2355 p_region_in->p_style, psz_text, &i_len,
2356 &i_runs, &pi_run_lengths, &pp_styles,
2358 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2361 p_region_out->i_x = p_region_in->i_x;
2362 p_region_out->i_y = p_region_in->i_y;
2364 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2366 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2367 pi_run_lengths, pp_styles, &p_lines, &result,
2368 b_karaoke, i_k_runs, pi_k_run_lengths,
2372 for( k=0; k<i_runs; k++)
2373 DeleteStyle( pp_styles[k] );
2375 free( pi_run_lengths );
2378 /* Don't attempt to render text that couldn't be layed out
2381 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2383 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2385 Render( p_filter, p_region_out, p_lines,
2386 result.x, result.y );
2390 RenderYUVA( p_filter, p_region_out, p_lines,
2391 result.x, result.y );
2395 FreeLines( p_lines );
2397 xml_ReaderDelete( p_xml, p_xml_reader );
2399 xml_Delete( p_xml );
2401 stream_Delete( p_sub );
2407 static char* FontConfig_Select( FcConfig* priv, const char* family,
2408 bool b_bold, bool b_italic, int *i_idx )
2411 FcPattern *pat, *p_pat;
2415 pat = FcPatternCreate();
2416 if (!pat) return NULL;
2418 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2419 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2420 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2421 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2423 FcDefaultSubstitute( pat );
2425 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2427 FcPatternDestroy( pat );
2431 p_pat = FcFontMatch( priv, pat, &result );
2432 FcPatternDestroy( pat );
2433 if( !p_pat ) return NULL;
2435 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2436 || ( val_b != FcTrue ) )
2438 FcPatternDestroy( p_pat );
2441 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2446 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2448 FcPatternDestroy( p_pat );
2453 if( strcasecmp((const char*)val_s, family ) != 0 )
2454 msg_Warn( p_filter, "fontconfig: selected font family is not"
2455 "the requested one: '%s' != '%s'\n",
2456 (const char*)val_s, family );
2459 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2461 FcPatternDestroy( p_pat );
2465 FcPatternDestroy( p_pat );
2466 return strdup( (const char*)val_s );
2470 static void FreeLine( line_desc_t *p_line )
2473 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2475 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2477 free( p_line->pp_glyphs );
2478 free( p_line->p_glyph_pos );
2479 free( p_line->p_fg_rgb );
2480 free( p_line->p_bg_rgb );
2481 free( p_line->p_fg_bg_ratio );
2482 free( p_line->pi_underline_offset );
2483 free( p_line->pi_underline_thickness );
2487 static void FreeLines( line_desc_t *p_lines )
2489 line_desc_t *p_line, *p_next;
2491 if( !p_lines ) return;
2493 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2495 p_next = p_line->p_next;
2500 static line_desc_t *NewLine( int i_count )
2502 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2504 if( !p_line ) return NULL;
2505 p_line->i_height = 0;
2506 p_line->i_width = 0;
2507 p_line->p_next = NULL;
2509 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2510 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2511 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2512 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2513 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2514 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2515 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2516 if( ( p_line->pp_glyphs == NULL ) ||
2517 ( p_line->p_glyph_pos == NULL ) ||
2518 ( p_line->p_fg_rgb == NULL ) ||
2519 ( p_line->p_bg_rgb == NULL ) ||
2520 ( p_line->p_fg_bg_ratio == NULL ) ||
2521 ( p_line->pi_underline_offset == NULL ) ||
2522 ( p_line->pi_underline_thickness == NULL ) )
2524 free( p_line->pi_underline_thickness );
2525 free( p_line->pi_underline_offset );
2526 free( p_line->p_fg_rgb );
2527 free( p_line->p_bg_rgb );
2528 free( p_line->p_fg_bg_ratio );
2529 free( p_line->p_glyph_pos );
2530 free( p_line->pp_glyphs );
2534 p_line->pp_glyphs[0] = NULL;
2535 p_line->b_new_color_mode = false;
2540 static int GetFontSize( filter_t *p_filter )
2542 filter_sys_t *p_sys = p_filter->p_sys;
2546 if( p_sys->i_default_font_size )
2548 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2549 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2551 i_size = p_sys->i_default_font_size;
2555 var_Get( p_filter, "freetype-rel-fontsize", &val );
2558 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2559 p_filter->p_sys->i_display_height =
2560 p_filter->fmt_out.video.i_height;
2565 msg_Warn( p_filter, "invalid fontsize, using 12" );
2566 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2567 i_size = 12 * val.i_int / 1000;
2574 static int SetFontSize( filter_t *p_filter, int i_size )
2576 filter_sys_t *p_sys = p_filter->p_sys;
2580 i_size = GetFontSize( p_filter );
2582 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2585 p_sys->i_font_size = i_size;
2587 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2589 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2590 return VLC_EGENERIC;
2596 static void YUVFromRGB( uint32_t i_argb,
2597 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2599 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2600 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2601 int i_blue = ( i_argb & 0x000000ff );
2603 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2604 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2605 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2606 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2607 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2608 -585 * i_blue + 4096 + 1048576) >> 13, 240);