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;
301 FcResult fontresult = FcResultNoMatch;
305 /* Allocate structure */
306 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
310 p_sys->p_library = 0;
311 p_sys->i_font_size = 0;
312 p_sys->i_display_height = 0;
314 var_Create( p_filter, "freetype-rel-fontsize",
315 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
317 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
318 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
319 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
320 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
321 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
322 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
323 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
326 if( !psz_fontfamily || !*psz_fontfamily )
328 #ifdef HAVE_FONTCONFIG
329 free( psz_fontfamily);
330 psz_fontfamily=strdup( DEFAULT_FONT );
332 free( psz_fontfamily );
333 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
334 if( !psz_fontfamily )
337 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
338 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
340 strcpy( psz_fontfamily, DEFAULT_FONT );
342 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
346 #ifdef HAVE_FONTCONFIG
347 /* Lets find some fontfile from freetype-font variable family */
349 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
352 fontpattern = FcPatternCreate();
357 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
358 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
359 free( psz_fontsize );
361 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
363 FcPatternDestroy( fontpattern );
366 FcDefaultSubstitute( fontpattern );
368 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
369 if( !fontmatch || fontresult == FcResultNoMatch )
371 FcPatternDestroy( fontpattern );
375 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
376 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
379 FcPatternDestroy( fontpattern );
380 FcPatternDestroy( fontmatch );
383 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile);
385 psz_fontfile = psz_fontfamily;
388 i_error = FT_Init_FreeType( &p_sys->p_library );
391 msg_Err( p_filter, "couldn't initialize freetype" );
395 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
396 fontindex, &p_sys->p_face );
398 if( i_error == FT_Err_Unknown_File_Format )
400 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
405 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
409 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
412 msg_Err( p_filter, "font has no unicode translation table" );
416 #ifdef HAVE_FONTCONFIG
417 p_sys->b_fontconfig_ok = false;
418 p_sys->p_fontconfig = NULL;
419 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
422 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
424 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
427 p_sys->pp_font_attachments = NULL;
428 p_sys->i_font_attachments = 0;
430 p_filter->pf_render_text = RenderText;
431 #ifdef HAVE_FONTCONFIG
432 p_filter->pf_render_html = RenderHtml;
433 FcPatternDestroy( fontmatch );
434 FcPatternDestroy( fontpattern );
436 p_filter->pf_render_html = NULL;
439 free( psz_fontfamily );
440 LoadFontsFromAttachments( p_filter );
445 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
446 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
447 free( psz_fontfile );
452 /*****************************************************************************
453 * Destroy: destroy Clone video thread output method
454 *****************************************************************************
455 * Clean up all data and library connections
456 *****************************************************************************/
457 static void Destroy( vlc_object_t *p_this )
459 filter_t *p_filter = (filter_t *)p_this;
460 filter_sys_t *p_sys = p_filter->p_sys;
462 if( p_sys->pp_font_attachments )
466 for( k = 0; k < p_sys->i_font_attachments; k++ )
467 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
469 free( p_sys->pp_font_attachments );
472 #ifdef HAVE_FONTCONFIG
473 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
476 /* FcFini asserts calling the subfunction FcCacheFini()
477 * even if no other library functions have been made since FcInit(),
478 * so don't call it. */
480 FT_Done_Face( p_sys->p_face );
481 FT_Done_FreeType( p_sys->p_library );
485 #ifdef HAVE_FONTCONFIG
486 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
488 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
490 /* Check for an existing Fontbuilder thread */
491 vlc_mutex_lock( &fb_lock );
492 vlc_object_t *p_fontbuilder =
493 vlc_object_find_name( p_filter->p_libvlc,
494 "fontlist builder", FIND_CHILD );
498 /* Create the FontBuilderThread thread as a child of a top-level
499 * object, so that it can survive the destruction of the
500 * freetype object - the fontlist only needs to be built once,
501 * and calling the fontbuild a second time while the first is
502 * still in progress can cause thread instabilities.
504 * XXX The fontbuilder will be destroy as soon as it is unused.
507 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
508 sizeof(vlc_object_t) );
511 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
512 p_fontbuilder->p_private = NULL;
513 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
515 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
517 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
518 var_SetBool( p_fontbuilder, "build-done", false );
519 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
520 var_SetBool( p_fontbuilder, "build-joined", false );
522 if( vlc_thread_create( p_fontbuilder,
525 VLC_THREAD_PRIORITY_LOW ) )
527 msg_Warn( p_filter, "fontconfig database builder thread can't "
528 "be launched. Font styling support will be limited." );
535 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
536 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
538 vlc_mutex_unlock( &fb_lock );
539 return p_fontbuilder;
541 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
543 vlc_mutex_lock( &fb_lock );
546 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
548 /* We wait for the thread on the first FontBuilderDetach */
549 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
551 var_SetBool( p_fontbuilder, "build-joined", true );
552 vlc_mutex_unlock( &fb_lock );
553 /* We need to unlock otherwise we may not join (the thread waiting
554 * for the lock). It is safe to unlock as no one else will try a
555 * join and we have a reference on the object) */
556 vlc_thread_join( p_fontbuilder );
557 vlc_mutex_lock( &fb_lock );
559 vlc_object_release( p_fontbuilder );
561 vlc_mutex_unlock( &fb_lock );
563 static void* FontBuilderThread( vlc_object_t *p_this )
565 FcConfig *p_fontconfig = FcInitLoadConfig();
570 int canc = vlc_savecancel ();
572 //msg_Dbg( p_this, "Building font database..." );
573 msg_Dbg( p_this, "Building font database..." );
575 if(! FcConfigBuildFonts( p_fontconfig ))
577 /* Don't destroy the fontconfig object - we won't be able to do
578 * italics or bold or change the font face, but we will still
579 * be able to do underline and change the font size.
581 msg_Err( p_this, "fontconfig database can't be built. "
582 "Font styling won't be available" );
586 msg_Dbg( p_this, "Finished building font database." );
587 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
589 vlc_mutex_lock( &fb_lock );
590 p_this->p_private = p_fontconfig;
591 vlc_mutex_unlock( &fb_lock );
593 var_SetBool( p_this, "build-done", true );
594 vlc_restorecancel (canc);
598 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
600 filter_sys_t *p_sys = p_filter->p_sys;
602 p_sys->p_fontconfig = p_fontbuilder->p_private;
603 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
605 static void FontBuilderDestructor( vlc_object_t *p_this )
607 FcConfig *p_fontconfig = p_this->p_private;
610 FcConfigDestroy( p_fontconfig );
612 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
613 vlc_value_t oldval, vlc_value_t newval, void *param )
615 filter_t *p_filter = param;
619 vlc_mutex_lock( &fb_lock );
621 FontBuilderGetFcConfig( p_filter, p_this );
623 vlc_mutex_unlock( &fb_lock );
632 /*****************************************************************************
633 * Make any TTF/OTF fonts present in the attachments of the media file
634 * and store them for later use by the FreeType Engine
635 *****************************************************************************/
636 static int LoadFontsFromAttachments( filter_t *p_filter )
638 filter_sys_t *p_sys = p_filter->p_sys;
639 input_thread_t *p_input;
640 input_attachment_t **pp_attachments;
641 int i_attachments_cnt;
643 int rv = VLC_SUCCESS;
645 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
649 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
651 vlc_object_release(p_input);
655 p_sys->i_font_attachments = 0;
656 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
657 if(! p_sys->pp_font_attachments )
660 for( k = 0; k < i_attachments_cnt; k++ )
662 input_attachment_t *p_attach = pp_attachments[k];
664 if( p_sys->pp_font_attachments )
666 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
667 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
668 ( p_attach->i_data > 0 ) &&
669 ( p_attach->p_data != NULL ) )
671 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
675 vlc_input_attachment_Delete( p_attach );
680 vlc_input_attachment_Delete( p_attach );
683 free( pp_attachments );
685 vlc_object_release(p_input);
690 /*****************************************************************************
691 * Render: place string in picture
692 *****************************************************************************
693 * This function merges the previously rendered freetype glyphs into a picture
694 *****************************************************************************/
695 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
696 line_desc_t *p_line, int i_width, int i_height )
698 static const uint8_t pi_gamma[16] =
699 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
700 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
704 int i, x, y, i_pitch;
705 uint8_t i_y; /* YUV values, derived from incoming RGB */
708 /* Create a new subpicture region */
709 memset( &fmt, 0, sizeof(video_format_t) );
710 fmt.i_chroma = VLC_CODEC_YUVP;
712 fmt.i_width = fmt.i_visible_width = i_width + 4;
713 fmt.i_height = fmt.i_visible_height = i_height + 4;
714 if( p_region->fmt.i_visible_width > 0 )
715 fmt.i_visible_width = p_region->fmt.i_visible_width;
716 if( p_region->fmt.i_visible_height > 0 )
717 fmt.i_visible_height = p_region->fmt.i_visible_height;
718 fmt.i_x_offset = fmt.i_y_offset = 0;
720 assert( !p_region->p_picture );
721 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
722 if( !p_region->p_picture )
726 /* Calculate text color components */
727 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
728 25 * p_line->i_blue + 128) >> 8) + 16;
729 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
730 112 * p_line->i_blue + 128) >> 8) + 128;
731 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
732 18 * p_line->i_blue + 128) >> 8) + 128;
735 fmt.p_palette->i_entries = 16;
736 for( i = 0; i < 8; i++ )
738 fmt.p_palette->palette[i][0] = 0;
739 fmt.p_palette->palette[i][1] = 0x80;
740 fmt.p_palette->palette[i][2] = 0x80;
741 fmt.p_palette->palette[i][3] = pi_gamma[i];
742 fmt.p_palette->palette[i][3] =
743 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
745 for( i = 8; i < fmt.p_palette->i_entries; i++ )
747 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
748 fmt.p_palette->palette[i][1] = i_u;
749 fmt.p_palette->palette[i][2] = i_v;
750 fmt.p_palette->palette[i][3] = pi_gamma[i];
751 fmt.p_palette->palette[i][3] =
752 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
755 p_dst = p_region->p_picture->Y_PIXELS;
756 i_pitch = p_region->p_picture->Y_PITCH;
758 /* Initialize the region pixels */
759 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
761 for( ; p_line != NULL; p_line = p_line->p_next )
763 int i_glyph_tmax = 0;
764 int i_bitmap_offset, i_offset, i_align_offset = 0;
765 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
767 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
768 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
771 if( p_line->i_width < i_width )
773 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
775 i_align_offset = i_width - p_line->i_width;
777 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
779 i_align_offset = ( i_width - p_line->i_width ) / 2;
783 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
785 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
787 i_offset = ( p_line->p_glyph_pos[ i ].y +
788 i_glyph_tmax - p_glyph->top + 2 ) *
789 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
792 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
794 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
796 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
798 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
805 /* Outlining (find something better than nearest neighbour filtering ?) */
808 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
809 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
810 uint8_t left, current;
812 for( y = 1; y < (int)fmt.i_height - 1; y++ )
814 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
815 p_dst += p_region->p_picture->Y_PITCH;
818 for( x = 1; x < (int)fmt.i_width - 1; x++ )
821 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
822 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;
826 memset( p_top, 0, fmt.i_width );
832 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
833 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
834 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
835 int i_glyph_tmax, int i_align_offset,
836 uint8_t i_y, uint8_t i_u, uint8_t i_v,
837 subpicture_region_t *p_region)
841 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
843 p_dst_y = p_region->p_picture->Y_PIXELS;
844 p_dst_u = p_region->p_picture->U_PIXELS;
845 p_dst_v = p_region->p_picture->V_PIXELS;
846 p_dst_a = p_region->p_picture->A_PIXELS;
847 i_pitch = p_region->p_picture->A_PITCH;
849 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
850 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
852 for( y = 0; y < i_line_thickness; y++ )
854 int i_extra = p_this_glyph->bitmap.width;
858 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
859 (p_this_glyph_pos->x + p_this_glyph->left);
861 for( x = 0; x < i_extra; x++ )
865 /* break the underline around the tails of any glyphs which cross it */
866 for( z = x - i_line_thickness;
867 z < x + i_line_thickness && b_ok;
870 if( p_next_glyph && ( z >= i_extra ) )
872 int i_row = i_line_offset + p_next_glyph->top + y;
874 if( ( p_next_glyph->bitmap.rows > i_row ) &&
875 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
880 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
882 int i_row = i_line_offset + p_this_glyph->top + y;
884 if( ( p_this_glyph->bitmap.rows > i_row ) &&
885 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
894 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
895 p_dst_u[i_offset+x] = i_u;
896 p_dst_v[i_offset+x] = i_v;
897 p_dst_a[i_offset+x] = 255;
904 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
906 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
907 int i_pitch = p_region->p_picture->A_PITCH;
910 for( ; p_line != NULL; p_line = p_line->p_next )
912 int i_glyph_tmax=0, i = 0;
913 int i_bitmap_offset, i_offset, i_align_offset = 0;
914 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
916 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
917 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
920 if( p_line->i_width < i_width )
922 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
924 i_align_offset = i_width - p_line->i_width;
926 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
928 i_align_offset = ( i_width - p_line->i_width ) / 2;
932 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
934 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
936 i_offset = ( p_line->p_glyph_pos[ i ].y +
937 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
938 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
939 i_align_offset +xoffset;
941 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
943 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
945 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
946 if( p_dst[i_offset+x] <
947 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
949 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
958 /*****************************************************************************
959 * Render: place string in picture
960 *****************************************************************************
961 * This function merges the previously rendered freetype glyphs into a picture
962 *****************************************************************************/
963 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
964 line_desc_t *p_line, int i_width, int i_height )
966 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
968 int i, x, y, i_pitch, i_alpha;
969 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
971 if( i_width == 0 || i_height == 0 )
974 /* Create a new subpicture region */
975 memset( &fmt, 0, sizeof(video_format_t) );
976 fmt.i_chroma = VLC_CODEC_YUVA;
978 fmt.i_width = fmt.i_visible_width = i_width + 6;
979 fmt.i_height = fmt.i_visible_height = i_height + 6;
980 if( p_region->fmt.i_visible_width > 0 )
981 fmt.i_visible_width = p_region->fmt.i_visible_width;
982 if( p_region->fmt.i_visible_height > 0 )
983 fmt.i_visible_height = p_region->fmt.i_visible_height;
984 fmt.i_x_offset = fmt.i_y_offset = 0;
986 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
987 if( !p_region->p_picture )
991 /* Calculate text color components */
992 YUVFromRGB( (p_line->i_red << 16) |
993 (p_line->i_green << 8) |
996 i_alpha = p_line->i_alpha;
998 p_dst_y = p_region->p_picture->Y_PIXELS;
999 p_dst_u = p_region->p_picture->U_PIXELS;
1000 p_dst_v = p_region->p_picture->V_PIXELS;
1001 p_dst_a = p_region->p_picture->A_PIXELS;
1002 i_pitch = p_region->p_picture->A_PITCH;
1004 /* Initialize the region pixels */
1005 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
1007 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
1008 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1009 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1010 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1014 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1015 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1016 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1017 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1019 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1020 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1022 DrawBlack( p_line, i_width, p_region, 0, 0);
1023 DrawBlack( p_line, i_width, p_region, -1, 0);
1024 DrawBlack( p_line, i_width, p_region, 0, -1);
1025 DrawBlack( p_line, i_width, p_region, 1, 0);
1026 DrawBlack( p_line, i_width, p_region, 0, 1);
1029 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
1034 DrawBlack( p_line, i_width, p_region, 1, 1);
1036 DrawBlack( p_line, i_width, p_region, -2, 0);
1037 DrawBlack( p_line, i_width, p_region, 0, -2);
1038 DrawBlack( p_line, i_width, p_region, 2, 0);
1039 DrawBlack( p_line, i_width, p_region, 0, 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);
1044 DrawBlack( p_line, i_width, p_region, 2, 2);
1046 DrawBlack( p_line, i_width, p_region, -3, 0);
1047 DrawBlack( p_line, i_width, p_region, 0, -3);
1048 DrawBlack( p_line, i_width, p_region, 3, 0);
1049 DrawBlack( p_line, i_width, p_region, 0, 3);
1052 for( ; p_line != NULL; p_line = p_line->p_next )
1054 int i_glyph_tmax = 0;
1055 int i_bitmap_offset, i_offset, i_align_offset = 0;
1056 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1058 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1059 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1062 if( p_line->i_width < i_width )
1064 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1066 i_align_offset = i_width - p_line->i_width;
1068 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1070 i_align_offset = ( i_width - p_line->i_width ) / 2;
1074 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1076 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1078 i_offset = ( p_line->p_glyph_pos[ i ].y +
1079 i_glyph_tmax - p_glyph->top + 3 ) *
1080 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1083 if( p_line->b_new_color_mode )
1085 /* Every glyph can (and in fact must) have its own color */
1086 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1089 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1091 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1093 uint8_t i_y_local = i_y;
1094 uint8_t i_u_local = i_u;
1095 uint8_t i_v_local = i_v;
1097 if( p_line->p_fg_bg_ratio != 0x00 )
1099 int i_split = p_glyph->bitmap.width *
1100 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1104 YUVFromRGB( p_line->p_bg_rgb[ i ],
1105 &i_y_local, &i_u_local, &i_v_local );
1109 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1111 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1112 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1114 p_dst_u[i_offset+x] = i_u;
1115 p_dst_v[i_offset+x] = i_v;
1117 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1118 p_dst_a[i_offset+x] = 0xff;
1121 i_offset += i_pitch;
1124 if( p_line->pi_underline_thickness[ i ] )
1126 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1127 p_line->pi_underline_offset[ i ],
1128 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1129 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1130 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1131 i_glyph_tmax, i_align_offset,
1138 /* Apply the alpha setting */
1139 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1140 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1146 * This function renders a text subpicture region into another one.
1147 * It also calculates the size needed for this string, and renders the
1148 * needed glyphs into memory. It is used as pf_add_string callback in
1149 * the vout method by this module
1151 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1152 subpicture_region_t *p_region_in )
1154 filter_sys_t *p_sys = p_filter->p_sys;
1155 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1156 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1157 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1158 int i_string_length;
1160 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1161 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1171 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1172 psz_string = p_region_in->psz_text;
1173 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1175 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1176 i_scale = val.i_int;
1178 if( p_region_in->p_style )
1180 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1181 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1182 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1186 i_font_color = p_sys->i_font_color;
1187 i_font_alpha = 255 - p_sys->i_font_opacity;
1188 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1191 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1192 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1193 SetFontSize( p_filter, i_font_size );
1195 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1196 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1197 i_blue = i_font_color & 0x000000FF;
1199 result.x = result.y = 0;
1200 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1202 psz_unicode = psz_unicode_orig =
1203 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1204 if( psz_unicode == NULL )
1206 #if defined(WORDS_BIGENDIAN)
1207 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1209 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1211 if( iconv_handle == (vlc_iconv_t)-1 )
1213 msg_Warn( p_filter, "unable to do conversion" );
1219 const char *p_in_buffer = psz_string;
1220 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1221 i_in_bytes = strlen( psz_string );
1222 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1223 i_out_bytes_left = i_out_bytes;
1224 p_out_buffer = (char *)psz_unicode;
1225 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1227 &p_out_buffer, &i_out_bytes_left );
1229 vlc_iconv_close( iconv_handle );
1233 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1234 "bytes left %u", (unsigned)i_in_bytes );
1237 *(uint32_t*)p_out_buffer = 0;
1238 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1241 #if defined(HAVE_FRIBIDI)
1243 uint32_t *p_fribidi_string;
1244 int32_t start_pos, pos = 0;
1246 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1247 if( !p_fribidi_string )
1250 /* Do bidi conversion line-by-line */
1251 while( pos < i_string_length )
1253 while( pos < i_string_length )
1255 i_char = psz_unicode[pos];
1256 if (i_char != '\r' && i_char != '\n')
1258 p_fribidi_string[pos] = i_char;
1262 while( pos < i_string_length )
1264 i_char = psz_unicode[pos];
1265 if (i_char == '\r' || i_char == '\n')
1269 if (pos > start_pos)
1271 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1272 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1275 (FriBidiChar*)p_fribidi_string + start_pos,
1280 free( psz_unicode_orig );
1281 psz_unicode = psz_unicode_orig = p_fribidi_string;
1282 p_fribidi_string[ i_string_length ] = 0;
1286 /* Calculate relative glyph positions and a bounding box for the
1288 if( !(p_line = NewLine( strlen( psz_string ))) )
1291 i_pen_x = i_pen_y = 0;
1293 psz_line_start = psz_unicode;
1295 #define face p_sys->p_face
1296 #define glyph face->glyph
1298 while( *psz_unicode )
1300 i_char = *psz_unicode++;
1301 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1306 if( i_char == '\n' )
1308 psz_line_start = psz_unicode;
1309 if( !(p_next = NewLine( strlen( psz_string ))) )
1311 p_line->p_next = p_next;
1312 p_line->i_width = line.xMax;
1313 p_line->i_height = face->size->metrics.height >> 6;
1314 p_line->pp_glyphs[ i ] = NULL;
1315 p_line->i_alpha = i_font_alpha;
1316 p_line->i_red = i_red;
1317 p_line->i_green = i_green;
1318 p_line->i_blue = i_blue;
1321 result.x = __MAX( result.x, line.xMax );
1322 result.y += face->size->metrics.height >> 6;
1325 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1326 i_pen_y += face->size->metrics.height >> 6;
1328 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1333 i_glyph_index = FT_Get_Char_Index( face, i_char );
1334 if( p_sys->i_use_kerning && i_glyph_index
1338 FT_Get_Kerning( face, i_previous, i_glyph_index,
1339 ft_kerning_default, &delta );
1340 i_pen_x += delta.x >> 6;
1343 p_line->p_glyph_pos[ i ].x = i_pen_x;
1344 p_line->p_glyph_pos[ i ].y = i_pen_y;
1345 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1348 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1352 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1355 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1359 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1360 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1363 FT_Done_Glyph( tmp_glyph );
1366 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1369 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1370 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1371 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1373 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1374 p_line->pp_glyphs[ i ] = NULL;
1376 p_line = NewLine( strlen( psz_string ));
1377 if( p_prev ) p_prev->p_next = p_line;
1378 else p_lines = p_line;
1380 uint32_t *psz_unicode_saved = psz_unicode;
1381 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1385 if( psz_unicode == psz_line_start )
1386 { /* try harder to break that line */
1387 psz_unicode = psz_unicode_saved;
1388 while( psz_unicode > psz_line_start &&
1389 *psz_unicode != '_' && *psz_unicode != '/' &&
1390 *psz_unicode != '\\' && *psz_unicode != '.' )
1395 if( psz_unicode == psz_line_start )
1397 msg_Warn( p_filter, "unbreakable string" );
1402 *psz_unicode = '\n';
1404 psz_unicode = psz_line_start;
1407 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1410 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1411 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1413 i_previous = i_glyph_index;
1414 i_pen_x += glyph->advance.x >> 6;
1418 p_line->i_width = line.xMax;
1419 p_line->i_height = face->size->metrics.height >> 6;
1420 p_line->pp_glyphs[ i ] = NULL;
1421 p_line->i_alpha = i_font_alpha;
1422 p_line->i_red = i_red;
1423 p_line->i_green = i_green;
1424 p_line->i_blue = i_blue;
1425 result.x = __MAX( result.x, line.xMax );
1426 result.y += line.yMax - line.yMin;
1431 p_region_out->i_x = p_region_in->i_x;
1432 p_region_out->i_y = p_region_in->i_y;
1434 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1435 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1437 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1439 free( psz_unicode_orig );
1440 FreeLines( p_lines );
1444 free( psz_unicode_orig );
1445 FreeLines( p_lines );
1446 return VLC_EGENERIC;
1449 #ifdef HAVE_FONTCONFIG
1450 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1451 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1452 bool b_italic, bool b_uline )
1454 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1458 p_style->i_font_size = i_font_size;
1459 p_style->i_font_color = i_font_color;
1460 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1461 p_style->b_italic = b_italic;
1462 p_style->b_bold = b_bold;
1463 p_style->b_underline = b_uline;
1465 p_style->psz_fontname = strdup( psz_fontname );
1470 static void DeleteStyle( ft_style_t *p_style )
1474 free( p_style->psz_fontname );
1479 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1486 if(( s1->i_font_size == s2->i_font_size ) &&
1487 ( s1->i_font_color == s2->i_font_color ) &&
1488 ( s1->b_italic == s2->b_italic ) &&
1489 ( s1->b_bold == s2->b_bold ) &&
1490 ( s1->b_underline == s2->b_underline ) &&
1491 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1498 static void IconvText( filter_t *p_filter, const char *psz_string,
1499 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1501 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1503 /* If memory hasn't been allocated for our output string, allocate it here
1504 * - the calling function must now be responsible for freeing it.
1506 if( !*ppsz_unicode )
1507 *ppsz_unicode = (uint32_t *)
1508 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1510 /* We don't need to handle a NULL pointer in *ppsz_unicode
1511 * if we are instead testing for a non NULL value like we are here */
1515 #if defined(WORDS_BIGENDIAN)
1516 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1518 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1520 if( iconv_handle != (vlc_iconv_t)-1 )
1522 char *p_in_buffer, *p_out_buffer;
1523 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1524 i_in_bytes = strlen( psz_string );
1525 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1526 i_out_bytes_left = i_out_bytes;
1527 p_in_buffer = (char *) psz_string;
1528 p_out_buffer = (char *) *ppsz_unicode;
1529 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1530 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1532 vlc_iconv_close( iconv_handle );
1536 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1537 "bytes left %u", (unsigned)i_in_bytes );
1541 *(uint32_t*)p_out_buffer = 0;
1543 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1548 msg_Warn( p_filter, "unable to do conversion" );
1553 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1554 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1557 ft_style_t *p_style = NULL;
1559 char *psz_fontname = NULL;
1560 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1561 uint32_t i_karaoke_bg_color = i_font_color;
1562 int i_font_size = p_sys->i_font_size;
1564 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1565 &i_font_color, &i_karaoke_bg_color ))
1567 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1568 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1573 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1574 bool b_uline, int i_karaoke_bgcolor,
1575 line_desc_t *p_line, uint32_t *psz_unicode,
1576 int *pi_pen_x, int i_pen_y, int *pi_start,
1577 FT_Vector *p_result )
1582 bool b_first_on_line = true;
1585 int i_pen_x_start = *pi_pen_x;
1587 uint32_t *psz_unicode_start = psz_unicode;
1589 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1591 /* Account for part of line already in position */
1592 for( i=0; i<*pi_start; i++ )
1596 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1597 ft_glyph_bbox_pixels, &glyph_size );
1599 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1600 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1601 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1602 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1608 b_first_on_line = false;
1610 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1616 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1617 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1621 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1622 ft_kerning_default, &delta );
1623 *pi_pen_x += delta.x >> 6;
1625 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1626 p_line->p_glyph_pos[ i ].y = i_pen_y;
1628 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1632 "unable to render text FT_Load_Glyph returned %d", i_error );
1633 p_line->pp_glyphs[ i ] = NULL;
1634 return VLC_EGENERIC;
1636 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1640 "unable to render text FT_Get_Glyph returned %d", i_error );
1641 p_line->pp_glyphs[ i ] = NULL;
1642 return VLC_EGENERIC;
1644 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1645 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1648 FT_Done_Glyph( tmp_glyph );
1653 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1654 p_face->size->metrics.y_scale));
1655 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1656 p_face->size->metrics.y_scale));
1658 p_line->pi_underline_offset[ i ] =
1659 ( aOffset < 0 ) ? -aOffset : aOffset;
1660 p_line->pi_underline_thickness[ i ] =
1661 ( aSize < 0 ) ? -aSize : aSize;
1663 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1664 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1665 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1666 p_line->p_fg_bg_ratio[ i ] = 0x00;
1668 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1669 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1670 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1672 for( ; i >= *pi_start; i-- )
1673 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1676 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1680 if( psz_unicode == psz_unicode_start )
1682 if( b_first_on_line )
1684 msg_Warn( p_filter, "unbreakable string" );
1685 p_line->pp_glyphs[ i ] = NULL;
1686 return VLC_EGENERIC;
1688 *pi_pen_x = i_pen_x_start;
1690 p_line->i_width = line.xMax;
1691 p_line->i_height = __MAX( p_line->i_height,
1692 p_face->size->metrics.height >> 6 );
1693 p_line->pp_glyphs[ i ] = NULL;
1695 p_result->x = __MAX( p_result->x, line.xMax );
1696 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1697 i_yMax - i_yMin ) );
1702 *psz_unicode = '\n';
1704 psz_unicode = psz_unicode_start;
1705 *pi_pen_x = i_pen_x_start;
1713 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1714 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1716 i_previous = i_glyph_index;
1717 *pi_pen_x += p_face->glyph->advance.x >> 6;
1720 p_line->i_width = line.xMax;
1721 p_line->i_height = __MAX( p_line->i_height,
1722 p_face->size->metrics.height >> 6 );
1723 p_line->pp_glyphs[ i ] = NULL;
1725 p_result->x = __MAX( p_result->x, line.xMax );
1726 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1727 line.yMax - line.yMin ) );
1731 /* Get rid of any text processed - if necessary repositioning
1732 * at the start of a new line of text
1736 *psz_unicode_start = '\0';
1738 else if( psz_unicode > psz_unicode_start )
1740 for( i=0; psz_unicode[ i ]; i++ )
1741 psz_unicode_start[ i ] = psz_unicode[ i ];
1742 psz_unicode_start[ i ] = '\0';
1748 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1749 uint32_t **psz_text_out, uint32_t *pi_runs,
1750 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1751 ft_style_t *p_style )
1753 uint32_t i_string_length = 0;
1755 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1756 *psz_text_out += i_string_length;
1758 if( ppp_styles && ppi_run_lengths )
1764 *ppp_styles = (ft_style_t **)
1765 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1767 else if( *pi_runs == 1 )
1769 *ppp_styles = (ft_style_t **)
1770 malloc( *pi_runs * sizeof( ft_style_t * ) );
1773 /* We have just malloc'ed this memory successfully -
1774 * *pi_runs HAS to be within the memory area of *ppp_styles */
1777 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1781 if( *ppi_run_lengths )
1783 *ppi_run_lengths = (uint32_t *)
1784 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1786 else if( *pi_runs == 1 )
1788 *ppi_run_lengths = (uint32_t *)
1789 malloc( *pi_runs * sizeof( uint32_t ) );
1792 /* same remarks here */
1793 if( *ppi_run_lengths )
1795 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1798 /* If we couldn't use the p_style argument due to memory allocation
1799 * problems above, release it here.
1801 if( p_style ) DeleteStyle( p_style );
1804 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1808 for( k=0; k < p_sys->i_font_attachments; k++ )
1810 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1812 FT_Face p_face = NULL;
1814 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1822 bool match = !strcasecmp( p_face->family_name,
1823 p_style->psz_fontname );
1825 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1826 match = match && p_style->b_bold;
1828 match = match && !p_style->b_bold;
1830 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1831 match = match && p_style->b_italic;
1833 match = match && !p_style->b_italic;
1841 FT_Done_Face( p_face );
1846 return VLC_EGENERIC;
1849 static int ProcessLines( filter_t *p_filter,
1854 uint32_t *pi_run_lengths,
1855 ft_style_t **pp_styles,
1856 line_desc_t **pp_lines,
1858 FT_Vector *p_result,
1862 uint32_t *pi_k_run_lengths,
1863 uint32_t *pi_k_durations )
1865 filter_sys_t *p_sys = p_filter->p_sys;
1866 ft_style_t **pp_char_styles;
1867 int *p_new_positions = NULL;
1868 int8_t *p_levels = NULL;
1869 uint8_t *pi_karaoke_bar = NULL;
1873 /* Assign each character in the text string its style explicitly, so that
1874 * after the characters have been shuffled around by Fribidi, we can re-apply
1875 * the styles, and to simplify the calculation of runs within a line.
1877 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1878 if( !pp_char_styles )
1883 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1884 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1885 * we just won't be able to display the progress bar; at least we'll
1891 for( j = 0; j < i_runs; j++ )
1892 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1893 pp_char_styles[ i++ ] = pp_styles[ j ];
1895 #if defined(HAVE_FRIBIDI)
1897 ft_style_t **pp_char_styles_new;
1898 int *p_old_positions;
1899 uint32_t *p_fribidi_string;
1900 int start_pos, pos = 0;
1902 pp_char_styles_new = (ft_style_t **)
1903 malloc( i_len * sizeof( ft_style_t * ));
1905 p_fribidi_string = (uint32_t *)
1906 malloc( (i_len + 1) * sizeof(uint32_t) );
1907 p_old_positions = (int *)
1908 malloc( (i_len + 1) * sizeof( int ) );
1909 p_new_positions = (int *)
1910 malloc( (i_len + 1) * sizeof( int ) );
1911 p_levels = (int8_t *)
1912 malloc( (i_len + 1) * sizeof( int8_t ) );
1914 if( ! pp_char_styles_new ||
1915 ! p_fribidi_string ||
1916 ! p_old_positions ||
1917 ! p_new_positions ||
1921 free( p_old_positions );
1922 free( p_new_positions );
1923 free( p_fribidi_string );
1924 free( pp_char_styles_new );
1925 free( pi_karaoke_bar );
1927 free( pp_char_styles );
1931 /* Do bidi conversion line-by-line */
1934 while(pos < i_len) {
1935 if (psz_text[pos] != '\n')
1937 p_fribidi_string[pos] = psz_text[pos];
1938 pp_char_styles_new[pos] = pp_char_styles[pos];
1939 p_new_positions[pos] = pos;
1944 while(pos < i_len) {
1945 if (psz_text[pos] == '\n')
1949 if (pos > start_pos)
1951 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1952 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1953 pos - start_pos, &base_dir,
1954 (FriBidiChar*)p_fribidi_string + start_pos,
1955 p_new_positions + start_pos,
1957 p_levels + start_pos );
1958 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1960 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1961 p_old_positions[ j - start_pos ] ];
1962 p_new_positions[ j ] += start_pos;
1966 free( p_old_positions );
1967 free( pp_char_styles );
1968 pp_char_styles = pp_char_styles_new;
1969 psz_text = p_fribidi_string;
1970 p_fribidi_string[ i_len ] = 0;
1973 /* Work out the karaoke */
1974 if( pi_karaoke_bar )
1976 int64_t i_last_duration = 0;
1977 int64_t i_duration = 0;
1978 int64_t i_start_pos = 0;
1979 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1981 for( k = 0; k< i_k_runs; k++ )
1983 double fraction = 0.0;
1985 i_duration += pi_k_durations[ k ];
1987 if( i_duration < i_elapsed )
1989 /* Completely finished this run-length -
1990 * let it render normally */
1994 else if( i_elapsed < i_last_duration )
1996 /* Haven't got up to this segment yet -
1997 * render it completely in karaoke BG mode */
2003 /* Partway through this run */
2005 fraction = (double)(i_elapsed - i_last_duration) /
2006 (double)pi_k_durations[ k ];
2008 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2010 double shade = pi_k_run_lengths[ k ] * fraction;
2012 if( p_new_positions )
2013 j = p_new_positions[ i_start_pos + i ];
2015 j = i_start_pos + i;
2017 if( i < (uint32_t)shade )
2018 pi_karaoke_bar[ j ] = 0xff;
2019 else if( (double)i > shade )
2020 pi_karaoke_bar[ j ] = 0x00;
2023 shade -= (int)shade;
2024 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2025 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2029 i_last_duration = i_duration;
2030 i_start_pos += pi_k_run_lengths[ k ];
2034 free( p_new_positions );
2036 FT_Vector tmp_result;
2038 line_desc_t *p_line = NULL;
2039 line_desc_t *p_prev = NULL;
2045 p_result->x = p_result->y = 0;
2046 tmp_result.x = tmp_result.y = 0;
2049 for( k = 0; k <= (uint32_t) i_len; k++ )
2051 if( ( k == (uint32_t) i_len ) ||
2053 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2055 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2057 /* End of the current style run */
2058 FT_Face p_face = NULL;
2061 /* Look for a match amongst our attachments first */
2062 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2066 char *psz_fontfile = NULL;
2068 vlc_mutex_lock( &fb_lock );
2069 if( p_sys->b_fontconfig_ok )
2071 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2072 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2073 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2074 p_style->psz_fontname,
2079 vlc_mutex_unlock( &fb_lock );
2081 if( psz_fontfile && ! *psz_fontfile )
2083 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2084 " so using default font", p_style->psz_fontname,
2085 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2086 (p_style->b_bold ? "(Bold)" :
2087 (p_style->b_italic ? "(Italic)" : ""))) );
2088 free( psz_fontfile );
2089 psz_fontfile = NULL;
2094 if( FT_New_Face( p_sys->p_library,
2095 psz_fontfile, i_idx, &p_face ) )
2097 free( psz_fontfile );
2098 free( pp_char_styles );
2099 #if defined(HAVE_FRIBIDI)
2102 free( pi_karaoke_bar );
2103 return VLC_EGENERIC;
2105 free( psz_fontfile );
2109 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2111 /* We've loaded a font face which is unhelpful for actually
2112 * rendering text - fallback to the default one.
2114 FT_Done_Face( p_face );
2118 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2119 ft_encoding_unicode ) ||
2120 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2121 p_style->i_font_size ) )
2123 if( p_face ) FT_Done_Face( p_face );
2124 free( pp_char_styles );
2125 #if defined(HAVE_FRIBIDI)
2128 free( pi_karaoke_bar );
2129 return VLC_EGENERIC;
2131 p_sys->i_use_kerning =
2132 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2135 uint32_t *psz_unicode = (uint32_t *)
2136 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2139 if( p_face ) FT_Done_Face( p_face );
2140 free( pp_char_styles );
2141 free( psz_unicode );
2142 #if defined(HAVE_FRIBIDI)
2145 free( pi_karaoke_bar );
2148 memcpy( psz_unicode, psz_text + i_prev,
2149 sizeof( uint32_t ) * ( k - i_prev ) );
2150 psz_unicode[ k - i_prev ] = 0;
2151 while( *psz_unicode )
2155 if( !(p_line = NewLine( i_len - i_prev)) )
2157 if( p_face ) FT_Done_Face( p_face );
2158 free( pp_char_styles );
2159 free( psz_unicode );
2160 #if defined(HAVE_FRIBIDI)
2163 free( pi_karaoke_bar );
2166 /* New Color mode only works in YUVA rendering mode --
2167 * (RGB mode has palette constraints on it). We therefore
2168 * need to populate the legacy colour fields also.
2170 p_line->b_new_color_mode = true;
2171 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2172 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2173 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2174 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2175 p_line->p_next = NULL;
2177 i_pen_y += tmp_result.y;
2181 if( p_prev ) p_prev->p_next = p_line;
2182 else *pp_lines = p_line;
2185 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2186 p_style->i_font_color, p_style->b_underline,
2187 p_style->i_karaoke_bg_color,
2188 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2189 &tmp_result ) != VLC_SUCCESS )
2191 if( p_face ) FT_Done_Face( p_face );
2192 free( pp_char_styles );
2193 free( psz_unicode );
2194 #if defined(HAVE_FRIBIDI)
2197 free( pi_karaoke_bar );
2198 return VLC_EGENERIC;
2203 p_result->x = __MAX( p_result->x, tmp_result.x );
2204 p_result->y += tmp_result.y;
2209 if( *psz_unicode == '\n')
2213 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2215 *c_ptr = *(c_ptr+1);
2220 free( psz_unicode );
2221 if( p_face ) FT_Done_Face( p_face );
2225 free( pp_char_styles );
2226 #if defined(HAVE_FRIBIDI)
2231 p_result->x = __MAX( p_result->x, tmp_result.x );
2232 p_result->y += tmp_result.y;
2235 if( pi_karaoke_bar )
2238 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2240 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2242 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2246 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2248 /* 100% BG colour will render faster if we
2249 * instead make it 100% FG colour, so leave
2250 * the ratio alone and copy the value across
2252 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2256 if( pi_karaoke_bar[ i ] & 0x80 )
2258 /* Swap Left and Right sides over for Right aligned
2259 * language text (eg. Arabic, Hebrew)
2261 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2263 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2264 p_line->p_bg_rgb[ k ] = i_tmp;
2266 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2269 /* Jump over the '\n' at the line-end */
2272 free( pi_karaoke_bar );
2278 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2279 subpicture_region_t *p_region_in )
2281 int rv = VLC_SUCCESS;
2282 stream_t *p_sub = NULL;
2283 xml_t *p_xml = NULL;
2284 xml_reader_t *p_xml_reader = NULL;
2286 if( !p_region_in || !p_region_in->psz_html )
2287 return VLC_EGENERIC;
2289 /* Reset the default fontsize in case screen metrics have changed */
2290 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2292 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2293 (uint8_t *) p_region_in->psz_html,
2294 strlen( p_region_in->psz_html ),
2298 p_xml = xml_Create( p_filter );
2301 bool b_karaoke = false;
2303 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2306 /* Look for Root Node */
2307 if( xml_ReaderRead( p_xml_reader ) == 1 )
2309 char *psz_node = xml_ReaderName( p_xml_reader );
2311 if( !strcasecmp( "karaoke", psz_node ) )
2313 /* We're going to have to render the text a number
2314 * of times to show the progress marker on the text.
2316 var_SetBool( p_filter, "text-rerender", true );
2319 else if( !strcasecmp( "text", psz_node ) )
2325 /* Only text and karaoke tags are supported */
2326 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2327 xml_ReaderDelete( p_xml, p_xml_reader );
2328 p_xml_reader = NULL;
2340 uint32_t i_runs = 0;
2341 uint32_t i_k_runs = 0;
2342 uint32_t *pi_run_lengths = NULL;
2343 uint32_t *pi_k_run_lengths = NULL;
2344 uint32_t *pi_k_durations = NULL;
2345 ft_style_t **pp_styles = NULL;
2347 line_desc_t *p_lines = NULL;
2349 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2350 sizeof( uint32_t ) );
2355 rv = ProcessNodes( p_filter, p_xml_reader,
2356 p_region_in->p_style, psz_text, &i_len,
2357 &i_runs, &pi_run_lengths, &pp_styles,
2359 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2362 p_region_out->i_x = p_region_in->i_x;
2363 p_region_out->i_y = p_region_in->i_y;
2365 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2367 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2368 pi_run_lengths, pp_styles, &p_lines, &result,
2369 b_karaoke, i_k_runs, pi_k_run_lengths,
2373 for( k=0; k<i_runs; k++)
2374 DeleteStyle( pp_styles[k] );
2376 free( pi_run_lengths );
2379 /* Don't attempt to render text that couldn't be layed out
2382 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2384 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2386 Render( p_filter, p_region_out, p_lines,
2387 result.x, result.y );
2391 RenderYUVA( p_filter, p_region_out, p_lines,
2392 result.x, result.y );
2396 FreeLines( p_lines );
2398 xml_ReaderDelete( p_xml, p_xml_reader );
2400 xml_Delete( p_xml );
2402 stream_Delete( p_sub );
2408 static char* FontConfig_Select( FcConfig* priv, const char* family,
2409 bool b_bold, bool b_italic, int *i_idx )
2412 FcPattern *pat, *p_pat;
2416 pat = FcPatternCreate();
2417 if (!pat) return NULL;
2419 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2420 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2421 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2422 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2424 FcDefaultSubstitute( pat );
2426 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2428 FcPatternDestroy( pat );
2432 p_pat = FcFontMatch( priv, pat, &result );
2433 FcPatternDestroy( pat );
2434 if( !p_pat ) return NULL;
2436 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2437 || ( val_b != FcTrue ) )
2439 FcPatternDestroy( p_pat );
2442 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2447 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2449 FcPatternDestroy( p_pat );
2454 if( strcasecmp((const char*)val_s, family ) != 0 )
2455 msg_Warn( p_filter, "fontconfig: selected font family is not"
2456 "the requested one: '%s' != '%s'\n",
2457 (const char*)val_s, family );
2460 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2462 FcPatternDestroy( p_pat );
2466 FcPatternDestroy( p_pat );
2467 return strdup( (const char*)val_s );
2471 static void FreeLine( line_desc_t *p_line )
2474 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2476 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2478 free( p_line->pp_glyphs );
2479 free( p_line->p_glyph_pos );
2480 free( p_line->p_fg_rgb );
2481 free( p_line->p_bg_rgb );
2482 free( p_line->p_fg_bg_ratio );
2483 free( p_line->pi_underline_offset );
2484 free( p_line->pi_underline_thickness );
2488 static void FreeLines( line_desc_t *p_lines )
2490 line_desc_t *p_line, *p_next;
2492 if( !p_lines ) return;
2494 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2496 p_next = p_line->p_next;
2501 static line_desc_t *NewLine( int i_count )
2503 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2505 if( !p_line ) return NULL;
2506 p_line->i_height = 0;
2507 p_line->i_width = 0;
2508 p_line->p_next = NULL;
2510 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2511 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2512 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2513 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2514 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2515 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2516 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2517 if( ( p_line->pp_glyphs == NULL ) ||
2518 ( p_line->p_glyph_pos == NULL ) ||
2519 ( p_line->p_fg_rgb == NULL ) ||
2520 ( p_line->p_bg_rgb == NULL ) ||
2521 ( p_line->p_fg_bg_ratio == NULL ) ||
2522 ( p_line->pi_underline_offset == NULL ) ||
2523 ( p_line->pi_underline_thickness == NULL ) )
2525 free( p_line->pi_underline_thickness );
2526 free( p_line->pi_underline_offset );
2527 free( p_line->p_fg_rgb );
2528 free( p_line->p_bg_rgb );
2529 free( p_line->p_fg_bg_ratio );
2530 free( p_line->p_glyph_pos );
2531 free( p_line->pp_glyphs );
2535 p_line->pp_glyphs[0] = NULL;
2536 p_line->b_new_color_mode = false;
2541 static int GetFontSize( filter_t *p_filter )
2543 filter_sys_t *p_sys = p_filter->p_sys;
2547 if( p_sys->i_default_font_size )
2549 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2550 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2552 i_size = p_sys->i_default_font_size;
2556 var_Get( p_filter, "freetype-rel-fontsize", &val );
2559 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2560 p_filter->p_sys->i_display_height =
2561 p_filter->fmt_out.video.i_height;
2566 msg_Warn( p_filter, "invalid fontsize, using 12" );
2567 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2568 i_size = 12 * val.i_int / 1000;
2575 static int SetFontSize( filter_t *p_filter, int i_size )
2577 filter_sys_t *p_sys = p_filter->p_sys;
2581 i_size = GetFontSize( p_filter );
2583 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2586 p_sys->i_font_size = i_size;
2588 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2590 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2591 return VLC_EGENERIC;
2597 static void YUVFromRGB( uint32_t i_argb,
2598 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2600 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2601 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2602 int i_blue = ( i_argb & 0x000000ff );
2604 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2605 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2606 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2607 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2608 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2609 -585 * i_blue + 4096 + 1048576) >> 13, 240);