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, *fontmatch;
306 /* Allocate structure */
307 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
311 p_sys->p_library = 0;
312 p_sys->i_font_size = 0;
313 p_sys->i_display_height = 0;
315 var_Create( p_filter, "freetype-font",
316 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
317 var_Create( p_filter, "freetype-fontsize",
318 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
319 var_Create( p_filter, "freetype-rel-fontsize",
320 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
321 var_Create( p_filter, "freetype-opacity",
322 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
323 var_Create( p_filter, "freetype-effect",
324 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
325 var_Get( p_filter, "freetype-opacity", &val );
326 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
327 var_Create( p_filter, "freetype-color",
328 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
329 var_Get( p_filter, "freetype-color", &val );
330 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
331 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
332 var_Get( p_filter, "freetype-fontsize", &val );
333 p_sys->i_default_font_size = val.i_int;
336 var_Get( p_filter, "freetype-font", &val );
337 psz_fontfamily = val.psz_string;
338 if( !psz_fontfamily || !*psz_fontfamily )
340 #ifdef HAVE_FONTCONFIG
341 free( psz_fontfamily);
342 psz_fontfamily=strdup( DEFAULT_FONT );
344 free( psz_fontfamily );
345 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
346 if( !psz_fontfamily )
349 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
350 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
352 strcpy( psz_fontfamily, DEFAULT_FONT );
354 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
358 #ifdef HAVE_FONTCONFIG
359 /* Lets find some fontfile from freetype-font variable family */
360 char *psz_fontsize = malloc( 4 );
364 snprintf( psz_fontsize, 4, "%d", p_sys->i_default_font_size );
365 fontpattern = FcPatternCreate();
366 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
367 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
369 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
371 FcPatternDestroy( fontpattern );
372 free( psz_fontsize );
375 FcDefaultSubstitute( fontpattern );
377 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
379 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
380 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
383 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile);
384 free( psz_fontsize );
386 psz_fontfile = psz_fontfamily;
389 i_error = FT_Init_FreeType( &p_sys->p_library );
392 msg_Err( p_filter, "couldn't initialize freetype" );
396 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
397 fontindex, &p_sys->p_face );
399 if( i_error == FT_Err_Unknown_File_Format )
401 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
406 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
410 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
413 msg_Err( p_filter, "font has no unicode translation table" );
417 #ifdef HAVE_FONTCONFIG
418 p_sys->b_fontconfig_ok = false;
419 p_sys->p_fontconfig = NULL;
420 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
423 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
425 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
428 p_sys->pp_font_attachments = NULL;
429 p_sys->i_font_attachments = 0;
431 p_filter->pf_render_text = RenderText;
432 #ifdef HAVE_FONTCONFIG
433 p_filter->pf_render_html = RenderHtml;
434 FcPatternDestroy( fontmatch );
435 FcPatternDestroy( fontpattern );
437 p_filter->pf_render_html = NULL;
440 free( psz_fontfamily );
441 LoadFontsFromAttachments( p_filter );
446 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
447 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
448 free( psz_fontfile );
453 /*****************************************************************************
454 * Destroy: destroy Clone video thread output method
455 *****************************************************************************
456 * Clean up all data and library connections
457 *****************************************************************************/
458 static void Destroy( vlc_object_t *p_this )
460 filter_t *p_filter = (filter_t *)p_this;
461 filter_sys_t *p_sys = p_filter->p_sys;
463 if( p_sys->pp_font_attachments )
467 for( k = 0; k < p_sys->i_font_attachments; k++ )
468 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
470 free( p_sys->pp_font_attachments );
473 #ifdef HAVE_FONTCONFIG
474 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
477 /* FcFini asserts calling the subfunction FcCacheFini()
478 * even if no other library functions have been made since FcInit(),
479 * so don't call it. */
481 FT_Done_Face( p_sys->p_face );
482 FT_Done_FreeType( p_sys->p_library );
486 #ifdef HAVE_FONTCONFIG
487 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
489 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
491 /* Check for an existing Fontbuilder thread */
492 vlc_mutex_lock( &fb_lock );
493 vlc_object_t *p_fontbuilder =
494 vlc_object_find_name( p_filter->p_libvlc,
495 "fontlist builder", FIND_CHILD );
499 /* Create the FontBuilderThread thread as a child of a top-level
500 * object, so that it can survive the destruction of the
501 * freetype object - the fontlist only needs to be built once,
502 * and calling the fontbuild a second time while the first is
503 * still in progress can cause thread instabilities.
505 * XXX The fontbuilder will be destroy as soon as it is unused.
508 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
509 sizeof(vlc_object_t) );
512 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
513 p_fontbuilder->p_private = NULL;
514 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
516 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
518 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
519 var_SetBool( p_fontbuilder, "build-done", false );
520 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
521 var_SetBool( p_fontbuilder, "build-joined", false );
523 if( vlc_thread_create( p_fontbuilder,
526 VLC_THREAD_PRIORITY_LOW ) )
528 msg_Warn( p_filter, "fontconfig database builder thread can't "
529 "be launched. Font styling support will be limited." );
536 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
537 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
539 vlc_mutex_unlock( &fb_lock );
540 return p_fontbuilder;
542 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
544 vlc_mutex_lock( &fb_lock );
547 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
549 /* We wait for the thread on the first FontBuilderDetach */
550 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
552 var_SetBool( p_fontbuilder, "build-joined", true );
553 vlc_mutex_unlock( &fb_lock );
554 /* We need to unlock otherwise we may not join (the thread waiting
555 * for the lock). It is safe to unlock as no one else will try a
556 * join and we have a reference on the object) */
557 vlc_thread_join( p_fontbuilder );
558 vlc_mutex_lock( &fb_lock );
560 vlc_object_release( p_fontbuilder );
562 vlc_mutex_unlock( &fb_lock );
564 static void* FontBuilderThread( vlc_object_t *p_this )
566 FcConfig *p_fontconfig = FcInitLoadConfig();
571 int canc = vlc_savecancel ();
573 //msg_Dbg( p_this, "Building font database..." );
574 msg_Dbg( p_this, "Building font database..." );
576 if(! FcConfigBuildFonts( p_fontconfig ))
578 /* Don't destroy the fontconfig object - we won't be able to do
579 * italics or bold or change the font face, but we will still
580 * be able to do underline and change the font size.
582 msg_Err( p_this, "fontconfig database can't be built. "
583 "Font styling won't be available" );
587 msg_Dbg( p_this, "Finished building font database." );
588 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
590 vlc_mutex_lock( &fb_lock );
591 p_this->p_private = p_fontconfig;
592 vlc_mutex_unlock( &fb_lock );
594 var_SetBool( p_this, "build-done", true );
595 vlc_restorecancel (canc);
599 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
601 filter_sys_t *p_sys = p_filter->p_sys;
603 p_sys->p_fontconfig = p_fontbuilder->p_private;
604 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
606 static void FontBuilderDestructor( vlc_object_t *p_this )
608 FcConfig *p_fontconfig = p_this->p_private;
611 FcConfigDestroy( p_fontconfig );
613 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
614 vlc_value_t oldval, vlc_value_t newval, void *param )
616 filter_t *p_filter = param;
620 vlc_mutex_lock( &fb_lock );
622 FontBuilderGetFcConfig( p_filter, p_this );
624 vlc_mutex_unlock( &fb_lock );
633 /*****************************************************************************
634 * Make any TTF/OTF fonts present in the attachments of the media file
635 * and store them for later use by the FreeType Engine
636 *****************************************************************************/
637 static int LoadFontsFromAttachments( filter_t *p_filter )
639 filter_sys_t *p_sys = p_filter->p_sys;
640 input_thread_t *p_input;
641 input_attachment_t **pp_attachments;
642 int i_attachments_cnt;
644 int rv = VLC_SUCCESS;
646 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
650 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
652 vlc_object_release(p_input);
656 p_sys->i_font_attachments = 0;
657 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
658 if(! p_sys->pp_font_attachments )
661 for( k = 0; k < i_attachments_cnt; k++ )
663 input_attachment_t *p_attach = pp_attachments[k];
665 if( p_sys->pp_font_attachments )
667 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
668 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
669 ( p_attach->i_data > 0 ) &&
670 ( p_attach->p_data != NULL ) )
672 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
676 vlc_input_attachment_Delete( p_attach );
681 vlc_input_attachment_Delete( p_attach );
684 free( pp_attachments );
686 vlc_object_release(p_input);
691 /*****************************************************************************
692 * Render: place string in picture
693 *****************************************************************************
694 * This function merges the previously rendered freetype glyphs into a picture
695 *****************************************************************************/
696 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
697 line_desc_t *p_line, int i_width, int i_height )
699 static const uint8_t pi_gamma[16] =
700 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
701 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
705 int i, x, y, i_pitch;
706 uint8_t i_y; /* YUV values, derived from incoming RGB */
709 /* Create a new subpicture region */
710 memset( &fmt, 0, sizeof(video_format_t) );
711 fmt.i_chroma = VLC_CODEC_YUVP;
713 fmt.i_width = fmt.i_visible_width = i_width + 4;
714 fmt.i_height = fmt.i_visible_height = i_height + 4;
715 if( p_region->fmt.i_visible_width > 0 )
716 fmt.i_visible_width = p_region->fmt.i_visible_width;
717 if( p_region->fmt.i_visible_height > 0 )
718 fmt.i_visible_height = p_region->fmt.i_visible_height;
719 fmt.i_x_offset = fmt.i_y_offset = 0;
721 assert( !p_region->p_picture );
722 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
723 if( !p_region->p_picture )
727 /* Calculate text color components */
728 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
729 25 * p_line->i_blue + 128) >> 8) + 16;
730 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
731 112 * p_line->i_blue + 128) >> 8) + 128;
732 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
733 18 * p_line->i_blue + 128) >> 8) + 128;
736 fmt.p_palette->i_entries = 16;
737 for( i = 0; i < 8; i++ )
739 fmt.p_palette->palette[i][0] = 0;
740 fmt.p_palette->palette[i][1] = 0x80;
741 fmt.p_palette->palette[i][2] = 0x80;
742 fmt.p_palette->palette[i][3] = pi_gamma[i];
743 fmt.p_palette->palette[i][3] =
744 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
746 for( i = 8; i < fmt.p_palette->i_entries; i++ )
748 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
749 fmt.p_palette->palette[i][1] = i_u;
750 fmt.p_palette->palette[i][2] = i_v;
751 fmt.p_palette->palette[i][3] = pi_gamma[i];
752 fmt.p_palette->palette[i][3] =
753 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
756 p_dst = p_region->p_picture->Y_PIXELS;
757 i_pitch = p_region->p_picture->Y_PITCH;
759 /* Initialize the region pixels */
760 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
762 for( ; p_line != NULL; p_line = p_line->p_next )
764 int i_glyph_tmax = 0;
765 int i_bitmap_offset, i_offset, i_align_offset = 0;
766 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
768 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
769 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
772 if( p_line->i_width < i_width )
774 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
776 i_align_offset = i_width - p_line->i_width;
778 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
780 i_align_offset = ( i_width - p_line->i_width ) / 2;
784 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
786 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
788 i_offset = ( p_line->p_glyph_pos[ i ].y +
789 i_glyph_tmax - p_glyph->top + 2 ) *
790 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
793 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
795 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
797 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
799 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
806 /* Outlining (find something better than nearest neighbour filtering ?) */
809 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
810 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
811 uint8_t left, current;
813 for( y = 1; y < (int)fmt.i_height - 1; y++ )
815 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
816 p_dst += p_region->p_picture->Y_PITCH;
819 for( x = 1; x < (int)fmt.i_width - 1; x++ )
822 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
823 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;
827 memset( p_top, 0, fmt.i_width );
833 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
834 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
835 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
836 int i_glyph_tmax, int i_align_offset,
837 uint8_t i_y, uint8_t i_u, uint8_t i_v,
838 subpicture_region_t *p_region)
842 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
844 p_dst_y = p_region->p_picture->Y_PIXELS;
845 p_dst_u = p_region->p_picture->U_PIXELS;
846 p_dst_v = p_region->p_picture->V_PIXELS;
847 p_dst_a = p_region->p_picture->A_PIXELS;
848 i_pitch = p_region->p_picture->A_PITCH;
850 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
851 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
853 for( y = 0; y < i_line_thickness; y++ )
855 int i_extra = p_this_glyph->bitmap.width;
859 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
860 (p_this_glyph_pos->x + p_this_glyph->left);
862 for( x = 0; x < i_extra; x++ )
866 /* break the underline around the tails of any glyphs which cross it */
867 for( z = x - i_line_thickness;
868 z < x + i_line_thickness && b_ok;
871 if( p_next_glyph && ( z >= i_extra ) )
873 int i_row = i_line_offset + p_next_glyph->top + y;
875 if( ( p_next_glyph->bitmap.rows > i_row ) &&
876 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
881 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
883 int i_row = i_line_offset + p_this_glyph->top + y;
885 if( ( p_this_glyph->bitmap.rows > i_row ) &&
886 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
895 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
896 p_dst_u[i_offset+x] = i_u;
897 p_dst_v[i_offset+x] = i_v;
898 p_dst_a[i_offset+x] = 255;
905 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
907 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
908 int i_pitch = p_region->p_picture->A_PITCH;
911 for( ; p_line != NULL; p_line = p_line->p_next )
913 int i_glyph_tmax=0, i = 0;
914 int i_bitmap_offset, i_offset, i_align_offset = 0;
915 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
917 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
918 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
921 if( p_line->i_width < i_width )
923 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
925 i_align_offset = i_width - p_line->i_width;
927 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
929 i_align_offset = ( i_width - p_line->i_width ) / 2;
933 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
935 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
937 i_offset = ( p_line->p_glyph_pos[ i ].y +
938 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
939 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
940 i_align_offset +xoffset;
942 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
944 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
946 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
947 if( p_dst[i_offset+x] <
948 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
950 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
959 /*****************************************************************************
960 * Render: place string in picture
961 *****************************************************************************
962 * This function merges the previously rendered freetype glyphs into a picture
963 *****************************************************************************/
964 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
965 line_desc_t *p_line, int i_width, int i_height )
967 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
969 int i, x, y, i_pitch, i_alpha;
970 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
972 if( i_width == 0 || i_height == 0 )
975 /* Create a new subpicture region */
976 memset( &fmt, 0, sizeof(video_format_t) );
977 fmt.i_chroma = VLC_CODEC_YUVA;
979 fmt.i_width = fmt.i_visible_width = i_width + 6;
980 fmt.i_height = fmt.i_visible_height = i_height + 6;
981 if( p_region->fmt.i_visible_width > 0 )
982 fmt.i_visible_width = p_region->fmt.i_visible_width;
983 if( p_region->fmt.i_visible_height > 0 )
984 fmt.i_visible_height = p_region->fmt.i_visible_height;
985 fmt.i_x_offset = fmt.i_y_offset = 0;
987 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
988 if( !p_region->p_picture )
992 /* Calculate text color components */
993 YUVFromRGB( (p_line->i_red << 16) |
994 (p_line->i_green << 8) |
997 i_alpha = p_line->i_alpha;
999 p_dst_y = p_region->p_picture->Y_PIXELS;
1000 p_dst_u = p_region->p_picture->U_PIXELS;
1001 p_dst_v = p_region->p_picture->V_PIXELS;
1002 p_dst_a = p_region->p_picture->A_PIXELS;
1003 i_pitch = p_region->p_picture->A_PITCH;
1005 /* Initialize the region pixels */
1006 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
1008 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
1009 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1010 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1011 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1015 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1016 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1017 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1018 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1020 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1021 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1023 DrawBlack( p_line, i_width, p_region, 0, 0);
1024 DrawBlack( p_line, i_width, p_region, -1, 0);
1025 DrawBlack( p_line, i_width, p_region, 0, -1);
1026 DrawBlack( p_line, i_width, p_region, 1, 0);
1027 DrawBlack( p_line, i_width, p_region, 0, 1);
1030 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1032 DrawBlack( p_line, i_width, p_region, -1, -1);
1033 DrawBlack( p_line, i_width, p_region, -1, 1);
1034 DrawBlack( p_line, i_width, p_region, 1, -1);
1035 DrawBlack( p_line, i_width, p_region, 1, 1);
1037 DrawBlack( p_line, i_width, p_region, -2, 0);
1038 DrawBlack( p_line, i_width, p_region, 0, -2);
1039 DrawBlack( p_line, i_width, p_region, 2, 0);
1040 DrawBlack( p_line, i_width, p_region, 0, 2);
1042 DrawBlack( p_line, i_width, p_region, -2, -2);
1043 DrawBlack( p_line, i_width, p_region, -2, 2);
1044 DrawBlack( p_line, i_width, p_region, 2, -2);
1045 DrawBlack( p_line, i_width, p_region, 2, 2);
1047 DrawBlack( p_line, i_width, p_region, -3, 0);
1048 DrawBlack( p_line, i_width, p_region, 0, -3);
1049 DrawBlack( p_line, i_width, p_region, 3, 0);
1050 DrawBlack( p_line, i_width, p_region, 0, 3);
1053 for( ; p_line != NULL; p_line = p_line->p_next )
1055 int i_glyph_tmax = 0;
1056 int i_bitmap_offset, i_offset, i_align_offset = 0;
1057 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1059 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1060 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1063 if( p_line->i_width < i_width )
1065 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1067 i_align_offset = i_width - p_line->i_width;
1069 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1071 i_align_offset = ( i_width - p_line->i_width ) / 2;
1075 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1077 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1079 i_offset = ( p_line->p_glyph_pos[ i ].y +
1080 i_glyph_tmax - p_glyph->top + 3 ) *
1081 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1084 if( p_line->b_new_color_mode )
1086 /* Every glyph can (and in fact must) have its own color */
1087 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1090 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1092 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1094 uint8_t i_y_local = i_y;
1095 uint8_t i_u_local = i_u;
1096 uint8_t i_v_local = i_v;
1098 if( p_line->p_fg_bg_ratio != 0x00 )
1100 int i_split = p_glyph->bitmap.width *
1101 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1105 YUVFromRGB( p_line->p_bg_rgb[ i ],
1106 &i_y_local, &i_u_local, &i_v_local );
1110 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1112 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1113 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1115 p_dst_u[i_offset+x] = i_u;
1116 p_dst_v[i_offset+x] = i_v;
1118 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1119 p_dst_a[i_offset+x] = 0xff;
1122 i_offset += i_pitch;
1125 if( p_line->pi_underline_thickness[ i ] )
1127 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1128 p_line->pi_underline_offset[ i ],
1129 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1130 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1131 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1132 i_glyph_tmax, i_align_offset,
1139 /* Apply the alpha setting */
1140 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1141 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1147 * This function renders a text subpicture region into another one.
1148 * It also calculates the size needed for this string, and renders the
1149 * needed glyphs into memory. It is used as pf_add_string callback in
1150 * the vout method by this module
1152 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1153 subpicture_region_t *p_region_in )
1155 filter_sys_t *p_sys = p_filter->p_sys;
1156 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1157 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1158 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1159 int i_string_length;
1161 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1162 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1172 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1173 psz_string = p_region_in->psz_text;
1174 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1176 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1177 i_scale = val.i_int;
1179 if( p_region_in->p_style )
1181 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1182 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1183 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1187 i_font_color = p_sys->i_font_color;
1188 i_font_alpha = 255 - p_sys->i_font_opacity;
1189 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1192 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1193 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1194 SetFontSize( p_filter, i_font_size );
1196 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1197 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1198 i_blue = i_font_color & 0x000000FF;
1200 result.x = result.y = 0;
1201 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1203 psz_unicode = psz_unicode_orig =
1204 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1205 if( psz_unicode == NULL )
1207 #if defined(WORDS_BIGENDIAN)
1208 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1210 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1212 if( iconv_handle == (vlc_iconv_t)-1 )
1214 msg_Warn( p_filter, "unable to do conversion" );
1220 const char *p_in_buffer = psz_string;
1221 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1222 i_in_bytes = strlen( psz_string );
1223 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1224 i_out_bytes_left = i_out_bytes;
1225 p_out_buffer = (char *)psz_unicode;
1226 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1228 &p_out_buffer, &i_out_bytes_left );
1230 vlc_iconv_close( iconv_handle );
1234 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1235 "bytes left %u", (unsigned)i_in_bytes );
1238 *(uint32_t*)p_out_buffer = 0;
1239 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1242 #if defined(HAVE_FRIBIDI)
1244 uint32_t *p_fribidi_string;
1245 int32_t start_pos, pos = 0;
1247 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1248 if( !p_fribidi_string )
1251 /* Do bidi conversion line-by-line */
1252 while( pos < i_string_length )
1254 while( pos < i_string_length )
1256 i_char = psz_unicode[pos];
1257 if (i_char != '\r' && i_char != '\n')
1259 p_fribidi_string[pos] = i_char;
1263 while( pos < i_string_length )
1265 i_char = psz_unicode[pos];
1266 if (i_char == '\r' || i_char == '\n')
1270 if (pos > start_pos)
1272 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1273 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1276 (FriBidiChar*)p_fribidi_string + start_pos,
1281 free( psz_unicode_orig );
1282 psz_unicode = psz_unicode_orig = p_fribidi_string;
1283 p_fribidi_string[ i_string_length ] = 0;
1287 /* Calculate relative glyph positions and a bounding box for the
1289 if( !(p_line = NewLine( strlen( psz_string ))) )
1292 i_pen_x = i_pen_y = 0;
1294 psz_line_start = psz_unicode;
1296 #define face p_sys->p_face
1297 #define glyph face->glyph
1299 while( *psz_unicode )
1301 i_char = *psz_unicode++;
1302 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1307 if( i_char == '\n' )
1309 psz_line_start = psz_unicode;
1310 if( !(p_next = NewLine( strlen( psz_string ))) )
1312 p_line->p_next = p_next;
1313 p_line->i_width = line.xMax;
1314 p_line->i_height = face->size->metrics.height >> 6;
1315 p_line->pp_glyphs[ i ] = NULL;
1316 p_line->i_alpha = i_font_alpha;
1317 p_line->i_red = i_red;
1318 p_line->i_green = i_green;
1319 p_line->i_blue = i_blue;
1322 result.x = __MAX( result.x, line.xMax );
1323 result.y += face->size->metrics.height >> 6;
1326 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1327 i_pen_y += face->size->metrics.height >> 6;
1329 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1334 i_glyph_index = FT_Get_Char_Index( face, i_char );
1335 if( p_sys->i_use_kerning && i_glyph_index
1339 FT_Get_Kerning( face, i_previous, i_glyph_index,
1340 ft_kerning_default, &delta );
1341 i_pen_x += delta.x >> 6;
1344 p_line->p_glyph_pos[ i ].x = i_pen_x;
1345 p_line->p_glyph_pos[ i ].y = i_pen_y;
1346 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1349 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1353 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1356 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1360 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1361 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1364 FT_Done_Glyph( tmp_glyph );
1367 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1370 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1371 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1372 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1374 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1375 p_line->pp_glyphs[ i ] = NULL;
1377 p_line = NewLine( strlen( psz_string ));
1378 if( p_prev ) p_prev->p_next = p_line;
1379 else p_lines = p_line;
1381 uint32_t *psz_unicode_saved = psz_unicode;
1382 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1386 if( psz_unicode == psz_line_start )
1387 { /* try harder to break that line */
1388 psz_unicode = psz_unicode_saved;
1389 while( psz_unicode > psz_line_start &&
1390 *psz_unicode != '_' && *psz_unicode != '/' &&
1391 *psz_unicode != '\\' && *psz_unicode != '.' )
1396 if( psz_unicode == psz_line_start )
1398 msg_Warn( p_filter, "unbreakable string" );
1403 *psz_unicode = '\n';
1405 psz_unicode = psz_line_start;
1408 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1411 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1412 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1414 i_previous = i_glyph_index;
1415 i_pen_x += glyph->advance.x >> 6;
1419 p_line->i_width = line.xMax;
1420 p_line->i_height = face->size->metrics.height >> 6;
1421 p_line->pp_glyphs[ i ] = NULL;
1422 p_line->i_alpha = i_font_alpha;
1423 p_line->i_red = i_red;
1424 p_line->i_green = i_green;
1425 p_line->i_blue = i_blue;
1426 result.x = __MAX( result.x, line.xMax );
1427 result.y += line.yMax - line.yMin;
1432 p_region_out->i_x = p_region_in->i_x;
1433 p_region_out->i_y = p_region_in->i_y;
1435 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1436 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1438 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1440 free( psz_unicode_orig );
1441 FreeLines( p_lines );
1445 free( psz_unicode_orig );
1446 FreeLines( p_lines );
1447 return VLC_EGENERIC;
1450 #ifdef HAVE_FONTCONFIG
1451 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1452 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1453 bool b_italic, bool b_uline )
1455 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1459 p_style->i_font_size = i_font_size;
1460 p_style->i_font_color = i_font_color;
1461 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1462 p_style->b_italic = b_italic;
1463 p_style->b_bold = b_bold;
1464 p_style->b_underline = b_uline;
1466 p_style->psz_fontname = strdup( psz_fontname );
1471 static void DeleteStyle( ft_style_t *p_style )
1475 free( p_style->psz_fontname );
1480 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1487 if(( s1->i_font_size == s2->i_font_size ) &&
1488 ( s1->i_font_color == s2->i_font_color ) &&
1489 ( s1->b_italic == s2->b_italic ) &&
1490 ( s1->b_bold == s2->b_bold ) &&
1491 ( s1->b_underline == s2->b_underline ) &&
1492 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1499 static void IconvText( filter_t *p_filter, const char *psz_string,
1500 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1502 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1504 /* If memory hasn't been allocated for our output string, allocate it here
1505 * - the calling function must now be responsible for freeing it.
1507 if( !*ppsz_unicode )
1508 *ppsz_unicode = (uint32_t *)
1509 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1511 /* We don't need to handle a NULL pointer in *ppsz_unicode
1512 * if we are instead testing for a non NULL value like we are here */
1516 #if defined(WORDS_BIGENDIAN)
1517 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1519 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1521 if( iconv_handle != (vlc_iconv_t)-1 )
1523 char *p_in_buffer, *p_out_buffer;
1524 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1525 i_in_bytes = strlen( psz_string );
1526 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1527 i_out_bytes_left = i_out_bytes;
1528 p_in_buffer = (char *) psz_string;
1529 p_out_buffer = (char *) *ppsz_unicode;
1530 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1531 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1533 vlc_iconv_close( iconv_handle );
1537 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1538 "bytes left %u", (unsigned)i_in_bytes );
1542 *(uint32_t*)p_out_buffer = 0;
1544 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1549 msg_Warn( p_filter, "unable to do conversion" );
1554 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1555 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1558 ft_style_t *p_style = NULL;
1560 char *psz_fontname = NULL;
1561 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1562 uint32_t i_karaoke_bg_color = i_font_color;
1563 int i_font_size = p_sys->i_font_size;
1565 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1566 &i_font_color, &i_karaoke_bg_color ))
1568 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1569 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1574 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1575 bool b_uline, int i_karaoke_bgcolor,
1576 line_desc_t *p_line, uint32_t *psz_unicode,
1577 int *pi_pen_x, int i_pen_y, int *pi_start,
1578 FT_Vector *p_result )
1583 bool b_first_on_line = true;
1586 int i_pen_x_start = *pi_pen_x;
1588 uint32_t *psz_unicode_start = psz_unicode;
1590 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1592 /* Account for part of line already in position */
1593 for( i=0; i<*pi_start; i++ )
1597 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1598 ft_glyph_bbox_pixels, &glyph_size );
1600 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1601 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1602 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1603 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1609 b_first_on_line = false;
1611 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1617 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1618 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1622 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1623 ft_kerning_default, &delta );
1624 *pi_pen_x += delta.x >> 6;
1626 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1627 p_line->p_glyph_pos[ i ].y = i_pen_y;
1629 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1633 "unable to render text FT_Load_Glyph returned %d", i_error );
1634 p_line->pp_glyphs[ i ] = NULL;
1635 return VLC_EGENERIC;
1637 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1641 "unable to render text FT_Get_Glyph returned %d", i_error );
1642 p_line->pp_glyphs[ i ] = NULL;
1643 return VLC_EGENERIC;
1645 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1646 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1649 FT_Done_Glyph( tmp_glyph );
1654 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1655 p_face->size->metrics.y_scale));
1656 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1657 p_face->size->metrics.y_scale));
1659 p_line->pi_underline_offset[ i ] =
1660 ( aOffset < 0 ) ? -aOffset : aOffset;
1661 p_line->pi_underline_thickness[ i ] =
1662 ( aSize < 0 ) ? -aSize : aSize;
1664 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1665 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1666 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1667 p_line->p_fg_bg_ratio[ i ] = 0x00;
1669 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1670 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1671 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1673 for( ; i >= *pi_start; i-- )
1674 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1677 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1681 if( psz_unicode == psz_unicode_start )
1683 if( b_first_on_line )
1685 msg_Warn( p_filter, "unbreakable string" );
1686 p_line->pp_glyphs[ i ] = NULL;
1687 return VLC_EGENERIC;
1689 *pi_pen_x = i_pen_x_start;
1691 p_line->i_width = line.xMax;
1692 p_line->i_height = __MAX( p_line->i_height,
1693 p_face->size->metrics.height >> 6 );
1694 p_line->pp_glyphs[ i ] = NULL;
1696 p_result->x = __MAX( p_result->x, line.xMax );
1697 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1698 i_yMax - i_yMin ) );
1703 *psz_unicode = '\n';
1705 psz_unicode = psz_unicode_start;
1706 *pi_pen_x = i_pen_x_start;
1714 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1715 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1717 i_previous = i_glyph_index;
1718 *pi_pen_x += p_face->glyph->advance.x >> 6;
1721 p_line->i_width = line.xMax;
1722 p_line->i_height = __MAX( p_line->i_height,
1723 p_face->size->metrics.height >> 6 );
1724 p_line->pp_glyphs[ i ] = NULL;
1726 p_result->x = __MAX( p_result->x, line.xMax );
1727 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1728 line.yMax - line.yMin ) );
1732 /* Get rid of any text processed - if necessary repositioning
1733 * at the start of a new line of text
1737 *psz_unicode_start = '\0';
1739 else if( psz_unicode > psz_unicode_start )
1741 for( i=0; psz_unicode[ i ]; i++ )
1742 psz_unicode_start[ i ] = psz_unicode[ i ];
1743 psz_unicode_start[ i ] = '\0';
1749 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1750 uint32_t **psz_text_out, uint32_t *pi_runs,
1751 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1752 ft_style_t *p_style )
1754 uint32_t i_string_length = 0;
1756 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1757 *psz_text_out += i_string_length;
1759 if( ppp_styles && ppi_run_lengths )
1765 *ppp_styles = (ft_style_t **)
1766 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1768 else if( *pi_runs == 1 )
1770 *ppp_styles = (ft_style_t **)
1771 malloc( *pi_runs * sizeof( ft_style_t * ) );
1774 /* We have just malloc'ed this memory successfully -
1775 * *pi_runs HAS to be within the memory area of *ppp_styles */
1778 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1782 if( *ppi_run_lengths )
1784 *ppi_run_lengths = (uint32_t *)
1785 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1787 else if( *pi_runs == 1 )
1789 *ppi_run_lengths = (uint32_t *)
1790 malloc( *pi_runs * sizeof( uint32_t ) );
1793 /* same remarks here */
1794 if( *ppi_run_lengths )
1796 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1799 /* If we couldn't use the p_style argument due to memory allocation
1800 * problems above, release it here.
1802 if( p_style ) DeleteStyle( p_style );
1805 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1809 for( k=0; k < p_sys->i_font_attachments; k++ )
1811 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1813 FT_Face p_face = NULL;
1815 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1823 bool match = !strcasecmp( p_face->family_name,
1824 p_style->psz_fontname );
1826 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1827 match = match && p_style->b_bold;
1829 match = match && !p_style->b_bold;
1831 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1832 match = match && p_style->b_italic;
1834 match = match && !p_style->b_italic;
1842 FT_Done_Face( p_face );
1847 return VLC_EGENERIC;
1850 static int ProcessLines( filter_t *p_filter,
1855 uint32_t *pi_run_lengths,
1856 ft_style_t **pp_styles,
1857 line_desc_t **pp_lines,
1859 FT_Vector *p_result,
1863 uint32_t *pi_k_run_lengths,
1864 uint32_t *pi_k_durations )
1866 filter_sys_t *p_sys = p_filter->p_sys;
1867 ft_style_t **pp_char_styles;
1868 int *p_new_positions = NULL;
1869 int8_t *p_levels = NULL;
1870 uint8_t *pi_karaoke_bar = NULL;
1874 /* Assign each character in the text string its style explicitly, so that
1875 * after the characters have been shuffled around by Fribidi, we can re-apply
1876 * the styles, and to simplify the calculation of runs within a line.
1878 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1879 if( !pp_char_styles )
1884 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1885 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1886 * we just won't be able to display the progress bar; at least we'll
1892 for( j = 0; j < i_runs; j++ )
1893 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1894 pp_char_styles[ i++ ] = pp_styles[ j ];
1896 #if defined(HAVE_FRIBIDI)
1898 ft_style_t **pp_char_styles_new;
1899 int *p_old_positions;
1900 uint32_t *p_fribidi_string;
1901 int start_pos, pos = 0;
1903 pp_char_styles_new = (ft_style_t **)
1904 malloc( i_len * sizeof( ft_style_t * ));
1906 p_fribidi_string = (uint32_t *)
1907 malloc( (i_len + 1) * sizeof(uint32_t) );
1908 p_old_positions = (int *)
1909 malloc( (i_len + 1) * sizeof( int ) );
1910 p_new_positions = (int *)
1911 malloc( (i_len + 1) * sizeof( int ) );
1912 p_levels = (int8_t *)
1913 malloc( (i_len + 1) * sizeof( int8_t ) );
1915 if( ! pp_char_styles_new ||
1916 ! p_fribidi_string ||
1917 ! p_old_positions ||
1918 ! p_new_positions ||
1922 free( p_old_positions );
1923 free( p_new_positions );
1924 free( p_fribidi_string );
1925 free( pp_char_styles_new );
1926 free( pi_karaoke_bar );
1928 free( pp_char_styles );
1932 /* Do bidi conversion line-by-line */
1935 while(pos < i_len) {
1936 if (psz_text[pos] != '\n')
1938 p_fribidi_string[pos] = psz_text[pos];
1939 pp_char_styles_new[pos] = pp_char_styles[pos];
1940 p_new_positions[pos] = pos;
1945 while(pos < i_len) {
1946 if (psz_text[pos] == '\n')
1950 if (pos > start_pos)
1952 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1953 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1954 pos - start_pos, &base_dir,
1955 (FriBidiChar*)p_fribidi_string + start_pos,
1956 p_new_positions + start_pos,
1958 p_levels + start_pos );
1959 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1961 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1962 p_old_positions[ j - start_pos ] ];
1963 p_new_positions[ j ] += start_pos;
1967 free( p_old_positions );
1968 free( pp_char_styles );
1969 pp_char_styles = pp_char_styles_new;
1970 psz_text = p_fribidi_string;
1971 p_fribidi_string[ i_len ] = 0;
1974 /* Work out the karaoke */
1975 if( pi_karaoke_bar )
1977 int64_t i_last_duration = 0;
1978 int64_t i_duration = 0;
1979 int64_t i_start_pos = 0;
1980 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1982 for( k = 0; k< i_k_runs; k++ )
1984 double fraction = 0.0;
1986 i_duration += pi_k_durations[ k ];
1988 if( i_duration < i_elapsed )
1990 /* Completely finished this run-length -
1991 * let it render normally */
1995 else if( i_elapsed < i_last_duration )
1997 /* Haven't got up to this segment yet -
1998 * render it completely in karaoke BG mode */
2004 /* Partway through this run */
2006 fraction = (double)(i_elapsed - i_last_duration) /
2007 (double)pi_k_durations[ k ];
2009 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2011 double shade = pi_k_run_lengths[ k ] * fraction;
2013 if( p_new_positions )
2014 j = p_new_positions[ i_start_pos + i ];
2016 j = i_start_pos + i;
2018 if( i < (uint32_t)shade )
2019 pi_karaoke_bar[ j ] = 0xff;
2020 else if( (double)i > shade )
2021 pi_karaoke_bar[ j ] = 0x00;
2024 shade -= (int)shade;
2025 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2026 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2030 i_last_duration = i_duration;
2031 i_start_pos += pi_k_run_lengths[ k ];
2035 free( p_new_positions );
2037 FT_Vector tmp_result;
2039 line_desc_t *p_line = NULL;
2040 line_desc_t *p_prev = NULL;
2046 p_result->x = p_result->y = 0;
2047 tmp_result.x = tmp_result.y = 0;
2050 for( k = 0; k <= (uint32_t) i_len; k++ )
2052 if( ( k == (uint32_t) i_len ) ||
2054 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2056 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2058 /* End of the current style run */
2059 FT_Face p_face = NULL;
2062 /* Look for a match amongst our attachments first */
2063 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2067 char *psz_fontfile = NULL;
2069 vlc_mutex_lock( &fb_lock );
2070 if( p_sys->b_fontconfig_ok )
2072 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2073 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2074 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2075 p_style->psz_fontname,
2080 vlc_mutex_unlock( &fb_lock );
2082 if( psz_fontfile && ! *psz_fontfile )
2084 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2085 " so using default font", p_style->psz_fontname,
2086 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2087 (p_style->b_bold ? "(Bold)" :
2088 (p_style->b_italic ? "(Italic)" : ""))) );
2089 free( psz_fontfile );
2090 psz_fontfile = NULL;
2095 if( FT_New_Face( p_sys->p_library,
2096 psz_fontfile, i_idx, &p_face ) )
2098 free( psz_fontfile );
2099 free( pp_char_styles );
2100 #if defined(HAVE_FRIBIDI)
2103 free( pi_karaoke_bar );
2104 return VLC_EGENERIC;
2106 free( psz_fontfile );
2110 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2112 /* We've loaded a font face which is unhelpful for actually
2113 * rendering text - fallback to the default one.
2115 FT_Done_Face( p_face );
2119 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2120 ft_encoding_unicode ) ||
2121 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2122 p_style->i_font_size ) )
2124 if( p_face ) FT_Done_Face( p_face );
2125 free( pp_char_styles );
2126 #if defined(HAVE_FRIBIDI)
2129 free( pi_karaoke_bar );
2130 return VLC_EGENERIC;
2132 p_sys->i_use_kerning =
2133 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2136 uint32_t *psz_unicode = (uint32_t *)
2137 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2140 if( p_face ) FT_Done_Face( p_face );
2141 free( pp_char_styles );
2142 free( psz_unicode );
2143 #if defined(HAVE_FRIBIDI)
2146 free( pi_karaoke_bar );
2149 memcpy( psz_unicode, psz_text + i_prev,
2150 sizeof( uint32_t ) * ( k - i_prev ) );
2151 psz_unicode[ k - i_prev ] = 0;
2152 while( *psz_unicode )
2156 if( !(p_line = NewLine( i_len - i_prev)) )
2158 if( p_face ) FT_Done_Face( p_face );
2159 free( pp_char_styles );
2160 free( psz_unicode );
2161 #if defined(HAVE_FRIBIDI)
2164 free( pi_karaoke_bar );
2167 /* New Color mode only works in YUVA rendering mode --
2168 * (RGB mode has palette constraints on it). We therefore
2169 * need to populate the legacy colour fields also.
2171 p_line->b_new_color_mode = true;
2172 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2173 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2174 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2175 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2176 p_line->p_next = NULL;
2178 i_pen_y += tmp_result.y;
2182 if( p_prev ) p_prev->p_next = p_line;
2183 else *pp_lines = p_line;
2186 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2187 p_style->i_font_color, p_style->b_underline,
2188 p_style->i_karaoke_bg_color,
2189 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2190 &tmp_result ) != VLC_SUCCESS )
2192 if( p_face ) FT_Done_Face( p_face );
2193 free( pp_char_styles );
2194 free( psz_unicode );
2195 #if defined(HAVE_FRIBIDI)
2198 free( pi_karaoke_bar );
2199 return VLC_EGENERIC;
2204 p_result->x = __MAX( p_result->x, tmp_result.x );
2205 p_result->y += tmp_result.y;
2210 if( *psz_unicode == '\n')
2214 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2216 *c_ptr = *(c_ptr+1);
2221 free( psz_unicode );
2222 if( p_face ) FT_Done_Face( p_face );
2226 free( pp_char_styles );
2227 #if defined(HAVE_FRIBIDI)
2232 p_result->x = __MAX( p_result->x, tmp_result.x );
2233 p_result->y += tmp_result.y;
2236 if( pi_karaoke_bar )
2239 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2241 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2243 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2247 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2249 /* 100% BG colour will render faster if we
2250 * instead make it 100% FG colour, so leave
2251 * the ratio alone and copy the value across
2253 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2257 if( pi_karaoke_bar[ i ] & 0x80 )
2259 /* Swap Left and Right sides over for Right aligned
2260 * language text (eg. Arabic, Hebrew)
2262 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2264 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2265 p_line->p_bg_rgb[ k ] = i_tmp;
2267 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2270 /* Jump over the '\n' at the line-end */
2273 free( pi_karaoke_bar );
2279 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2280 subpicture_region_t *p_region_in )
2282 int rv = VLC_SUCCESS;
2283 stream_t *p_sub = NULL;
2284 xml_t *p_xml = NULL;
2285 xml_reader_t *p_xml_reader = NULL;
2287 if( !p_region_in || !p_region_in->psz_html )
2288 return VLC_EGENERIC;
2290 /* Reset the default fontsize in case screen metrics have changed */
2291 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2293 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2294 (uint8_t *) p_region_in->psz_html,
2295 strlen( p_region_in->psz_html ),
2299 p_xml = xml_Create( p_filter );
2302 bool b_karaoke = false;
2304 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2307 /* Look for Root Node */
2308 if( xml_ReaderRead( p_xml_reader ) == 1 )
2310 char *psz_node = xml_ReaderName( p_xml_reader );
2312 if( !strcasecmp( "karaoke", psz_node ) )
2314 /* We're going to have to render the text a number
2315 * of times to show the progress marker on the text.
2317 var_SetBool( p_filter, "text-rerender", true );
2320 else if( !strcasecmp( "text", psz_node ) )
2326 /* Only text and karaoke tags are supported */
2327 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2328 xml_ReaderDelete( p_xml, p_xml_reader );
2329 p_xml_reader = NULL;
2341 uint32_t i_runs = 0;
2342 uint32_t i_k_runs = 0;
2343 uint32_t *pi_run_lengths = NULL;
2344 uint32_t *pi_k_run_lengths = NULL;
2345 uint32_t *pi_k_durations = NULL;
2346 ft_style_t **pp_styles = NULL;
2348 line_desc_t *p_lines = NULL;
2350 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2351 sizeof( uint32_t ) );
2356 rv = ProcessNodes( p_filter, p_xml_reader,
2357 p_region_in->p_style, psz_text, &i_len,
2358 &i_runs, &pi_run_lengths, &pp_styles,
2360 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2363 p_region_out->i_x = p_region_in->i_x;
2364 p_region_out->i_y = p_region_in->i_y;
2366 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2368 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2369 pi_run_lengths, pp_styles, &p_lines, &result,
2370 b_karaoke, i_k_runs, pi_k_run_lengths,
2374 for( k=0; k<i_runs; k++)
2375 DeleteStyle( pp_styles[k] );
2377 free( pi_run_lengths );
2380 /* Don't attempt to render text that couldn't be layed out
2383 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2385 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2387 Render( p_filter, p_region_out, p_lines,
2388 result.x, result.y );
2392 RenderYUVA( p_filter, p_region_out, p_lines,
2393 result.x, result.y );
2397 FreeLines( p_lines );
2399 xml_ReaderDelete( p_xml, p_xml_reader );
2401 xml_Delete( p_xml );
2403 stream_Delete( p_sub );
2409 static char* FontConfig_Select( FcConfig* priv, const char* family,
2410 bool b_bold, bool b_italic, int *i_idx )
2413 FcPattern *pat, *p_pat;
2417 pat = FcPatternCreate();
2418 if (!pat) return NULL;
2420 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2421 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2422 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2423 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2425 FcDefaultSubstitute( pat );
2427 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2429 FcPatternDestroy( pat );
2433 p_pat = FcFontMatch( priv, pat, &result );
2434 FcPatternDestroy( pat );
2435 if( !p_pat ) return NULL;
2437 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2438 || ( val_b != FcTrue ) )
2440 FcPatternDestroy( p_pat );
2443 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2448 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2450 FcPatternDestroy( p_pat );
2455 if( strcasecmp((const char*)val_s, family ) != 0 )
2456 msg_Warn( p_filter, "fontconfig: selected font family is not"
2457 "the requested one: '%s' != '%s'\n",
2458 (const char*)val_s, family );
2461 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2463 FcPatternDestroy( p_pat );
2467 FcPatternDestroy( p_pat );
2468 return strdup( (const char*)val_s );
2472 static void FreeLine( line_desc_t *p_line )
2475 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2477 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2479 free( p_line->pp_glyphs );
2480 free( p_line->p_glyph_pos );
2481 free( p_line->p_fg_rgb );
2482 free( p_line->p_bg_rgb );
2483 free( p_line->p_fg_bg_ratio );
2484 free( p_line->pi_underline_offset );
2485 free( p_line->pi_underline_thickness );
2489 static void FreeLines( line_desc_t *p_lines )
2491 line_desc_t *p_line, *p_next;
2493 if( !p_lines ) return;
2495 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2497 p_next = p_line->p_next;
2502 static line_desc_t *NewLine( int i_count )
2504 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2506 if( !p_line ) return NULL;
2507 p_line->i_height = 0;
2508 p_line->i_width = 0;
2509 p_line->p_next = NULL;
2511 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2512 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2513 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2514 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2515 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2516 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2517 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2518 if( ( p_line->pp_glyphs == NULL ) ||
2519 ( p_line->p_glyph_pos == NULL ) ||
2520 ( p_line->p_fg_rgb == NULL ) ||
2521 ( p_line->p_bg_rgb == NULL ) ||
2522 ( p_line->p_fg_bg_ratio == NULL ) ||
2523 ( p_line->pi_underline_offset == NULL ) ||
2524 ( p_line->pi_underline_thickness == NULL ) )
2526 free( p_line->pi_underline_thickness );
2527 free( p_line->pi_underline_offset );
2528 free( p_line->p_fg_rgb );
2529 free( p_line->p_bg_rgb );
2530 free( p_line->p_fg_bg_ratio );
2531 free( p_line->p_glyph_pos );
2532 free( p_line->pp_glyphs );
2536 p_line->pp_glyphs[0] = NULL;
2537 p_line->b_new_color_mode = false;
2542 static int GetFontSize( filter_t *p_filter )
2544 filter_sys_t *p_sys = p_filter->p_sys;
2548 if( p_sys->i_default_font_size )
2550 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2551 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2553 i_size = p_sys->i_default_font_size;
2557 var_Get( p_filter, "freetype-rel-fontsize", &val );
2560 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2561 p_filter->p_sys->i_display_height =
2562 p_filter->fmt_out.video.i_height;
2567 msg_Warn( p_filter, "invalid fontsize, using 12" );
2568 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2569 i_size = 12 * val.i_int / 1000;
2576 static int SetFontSize( filter_t *p_filter, int i_size )
2578 filter_sys_t *p_sys = p_filter->p_sys;
2582 i_size = GetFontSize( p_filter );
2584 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2587 p_sys->i_font_size = i_size;
2589 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2591 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2592 return VLC_EGENERIC;
2598 static void YUVFromRGB( uint32_t i_argb,
2599 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2601 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2602 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2603 int i_blue = ( i_argb & 0x000000ff );
2605 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2606 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2607 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2608 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2609 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2610 -585 * i_blue + 4096 + 1048576) >> 13, 240);