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;
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 )
351 fontpattern = FcPatternCreate();
352 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
353 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
355 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
357 FcPatternDestroy( fontpattern );
358 free( psz_fontsize );
361 FcDefaultSubstitute( fontpattern );
363 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
364 if( fontmatch == FcResultNoMath )
366 free( psz_fontsize );
367 FcPatternDestroy( fontpattern );
368 FcPatternDestroy( fontmatch );
372 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
373 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
374 free( psz_fontsize );
377 FcPatternDestroy( fontpattern );
378 FcPatternDestroy( fontmatch );
381 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile);
383 psz_fontfile = psz_fontfamily;
386 i_error = FT_Init_FreeType( &p_sys->p_library );
389 msg_Err( p_filter, "couldn't initialize freetype" );
393 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
394 fontindex, &p_sys->p_face );
396 if( i_error == FT_Err_Unknown_File_Format )
398 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
403 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
407 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
410 msg_Err( p_filter, "font has no unicode translation table" );
414 #ifdef HAVE_FONTCONFIG
415 p_sys->b_fontconfig_ok = false;
416 p_sys->p_fontconfig = NULL;
417 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
420 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
422 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
425 p_sys->pp_font_attachments = NULL;
426 p_sys->i_font_attachments = 0;
428 p_filter->pf_render_text = RenderText;
429 #ifdef HAVE_FONTCONFIG
430 p_filter->pf_render_html = RenderHtml;
431 FcPatternDestroy( fontmatch );
432 FcPatternDestroy( fontpattern );
434 p_filter->pf_render_html = NULL;
437 free( psz_fontfamily );
438 LoadFontsFromAttachments( p_filter );
443 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
444 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
445 free( psz_fontfile );
450 /*****************************************************************************
451 * Destroy: destroy Clone video thread output method
452 *****************************************************************************
453 * Clean up all data and library connections
454 *****************************************************************************/
455 static void Destroy( vlc_object_t *p_this )
457 filter_t *p_filter = (filter_t *)p_this;
458 filter_sys_t *p_sys = p_filter->p_sys;
460 if( p_sys->pp_font_attachments )
464 for( k = 0; k < p_sys->i_font_attachments; k++ )
465 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
467 free( p_sys->pp_font_attachments );
470 #ifdef HAVE_FONTCONFIG
471 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
474 /* FcFini asserts calling the subfunction FcCacheFini()
475 * even if no other library functions have been made since FcInit(),
476 * so don't call it. */
478 FT_Done_Face( p_sys->p_face );
479 FT_Done_FreeType( p_sys->p_library );
483 #ifdef HAVE_FONTCONFIG
484 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
486 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
488 /* Check for an existing Fontbuilder thread */
489 vlc_mutex_lock( &fb_lock );
490 vlc_object_t *p_fontbuilder =
491 vlc_object_find_name( p_filter->p_libvlc,
492 "fontlist builder", FIND_CHILD );
496 /* Create the FontBuilderThread thread as a child of a top-level
497 * object, so that it can survive the destruction of the
498 * freetype object - the fontlist only needs to be built once,
499 * and calling the fontbuild a second time while the first is
500 * still in progress can cause thread instabilities.
502 * XXX The fontbuilder will be destroy as soon as it is unused.
505 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
506 sizeof(vlc_object_t) );
509 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
510 p_fontbuilder->p_private = NULL;
511 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
513 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
515 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
516 var_SetBool( p_fontbuilder, "build-done", false );
517 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
518 var_SetBool( p_fontbuilder, "build-joined", false );
520 if( vlc_thread_create( p_fontbuilder,
523 VLC_THREAD_PRIORITY_LOW ) )
525 msg_Warn( p_filter, "fontconfig database builder thread can't "
526 "be launched. Font styling support will be limited." );
533 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
534 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
536 vlc_mutex_unlock( &fb_lock );
537 return p_fontbuilder;
539 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
541 vlc_mutex_lock( &fb_lock );
544 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
546 /* We wait for the thread on the first FontBuilderDetach */
547 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
549 var_SetBool( p_fontbuilder, "build-joined", true );
550 vlc_mutex_unlock( &fb_lock );
551 /* We need to unlock otherwise we may not join (the thread waiting
552 * for the lock). It is safe to unlock as no one else will try a
553 * join and we have a reference on the object) */
554 vlc_thread_join( p_fontbuilder );
555 vlc_mutex_lock( &fb_lock );
557 vlc_object_release( p_fontbuilder );
559 vlc_mutex_unlock( &fb_lock );
561 static void* FontBuilderThread( vlc_object_t *p_this )
563 FcConfig *p_fontconfig = FcInitLoadConfig();
568 int canc = vlc_savecancel ();
570 //msg_Dbg( p_this, "Building font database..." );
571 msg_Dbg( p_this, "Building font database..." );
573 if(! FcConfigBuildFonts( p_fontconfig ))
575 /* Don't destroy the fontconfig object - we won't be able to do
576 * italics or bold or change the font face, but we will still
577 * be able to do underline and change the font size.
579 msg_Err( p_this, "fontconfig database can't be built. "
580 "Font styling won't be available" );
584 msg_Dbg( p_this, "Finished building font database." );
585 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
587 vlc_mutex_lock( &fb_lock );
588 p_this->p_private = p_fontconfig;
589 vlc_mutex_unlock( &fb_lock );
591 var_SetBool( p_this, "build-done", true );
592 vlc_restorecancel (canc);
596 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
598 filter_sys_t *p_sys = p_filter->p_sys;
600 p_sys->p_fontconfig = p_fontbuilder->p_private;
601 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
603 static void FontBuilderDestructor( vlc_object_t *p_this )
605 FcConfig *p_fontconfig = p_this->p_private;
608 FcConfigDestroy( p_fontconfig );
610 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
611 vlc_value_t oldval, vlc_value_t newval, void *param )
613 filter_t *p_filter = param;
617 vlc_mutex_lock( &fb_lock );
619 FontBuilderGetFcConfig( p_filter, p_this );
621 vlc_mutex_unlock( &fb_lock );
630 /*****************************************************************************
631 * Make any TTF/OTF fonts present in the attachments of the media file
632 * and store them for later use by the FreeType Engine
633 *****************************************************************************/
634 static int LoadFontsFromAttachments( filter_t *p_filter )
636 filter_sys_t *p_sys = p_filter->p_sys;
637 input_thread_t *p_input;
638 input_attachment_t **pp_attachments;
639 int i_attachments_cnt;
641 int rv = VLC_SUCCESS;
643 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
647 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
649 vlc_object_release(p_input);
653 p_sys->i_font_attachments = 0;
654 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
655 if(! p_sys->pp_font_attachments )
658 for( k = 0; k < i_attachments_cnt; k++ )
660 input_attachment_t *p_attach = pp_attachments[k];
662 if( p_sys->pp_font_attachments )
664 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
665 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
666 ( p_attach->i_data > 0 ) &&
667 ( p_attach->p_data != NULL ) )
669 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
673 vlc_input_attachment_Delete( p_attach );
678 vlc_input_attachment_Delete( p_attach );
681 free( pp_attachments );
683 vlc_object_release(p_input);
688 /*****************************************************************************
689 * Render: place string in picture
690 *****************************************************************************
691 * This function merges the previously rendered freetype glyphs into a picture
692 *****************************************************************************/
693 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
694 line_desc_t *p_line, int i_width, int i_height )
696 static const uint8_t pi_gamma[16] =
697 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
698 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
702 int i, x, y, i_pitch;
703 uint8_t i_y; /* YUV values, derived from incoming RGB */
706 /* Create a new subpicture region */
707 memset( &fmt, 0, sizeof(video_format_t) );
708 fmt.i_chroma = VLC_CODEC_YUVP;
710 fmt.i_width = fmt.i_visible_width = i_width + 4;
711 fmt.i_height = fmt.i_visible_height = i_height + 4;
712 if( p_region->fmt.i_visible_width > 0 )
713 fmt.i_visible_width = p_region->fmt.i_visible_width;
714 if( p_region->fmt.i_visible_height > 0 )
715 fmt.i_visible_height = p_region->fmt.i_visible_height;
716 fmt.i_x_offset = fmt.i_y_offset = 0;
718 assert( !p_region->p_picture );
719 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
720 if( !p_region->p_picture )
724 /* Calculate text color components */
725 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
726 25 * p_line->i_blue + 128) >> 8) + 16;
727 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
728 112 * p_line->i_blue + 128) >> 8) + 128;
729 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
730 18 * p_line->i_blue + 128) >> 8) + 128;
733 fmt.p_palette->i_entries = 16;
734 for( i = 0; i < 8; i++ )
736 fmt.p_palette->palette[i][0] = 0;
737 fmt.p_palette->palette[i][1] = 0x80;
738 fmt.p_palette->palette[i][2] = 0x80;
739 fmt.p_palette->palette[i][3] = pi_gamma[i];
740 fmt.p_palette->palette[i][3] =
741 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
743 for( i = 8; i < fmt.p_palette->i_entries; i++ )
745 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
746 fmt.p_palette->palette[i][1] = i_u;
747 fmt.p_palette->palette[i][2] = i_v;
748 fmt.p_palette->palette[i][3] = pi_gamma[i];
749 fmt.p_palette->palette[i][3] =
750 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
753 p_dst = p_region->p_picture->Y_PIXELS;
754 i_pitch = p_region->p_picture->Y_PITCH;
756 /* Initialize the region pixels */
757 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
759 for( ; p_line != NULL; p_line = p_line->p_next )
761 int i_glyph_tmax = 0;
762 int i_bitmap_offset, i_offset, i_align_offset = 0;
763 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
765 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
766 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
769 if( p_line->i_width < i_width )
771 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
773 i_align_offset = i_width - p_line->i_width;
775 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
777 i_align_offset = ( i_width - p_line->i_width ) / 2;
781 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
783 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
785 i_offset = ( p_line->p_glyph_pos[ i ].y +
786 i_glyph_tmax - p_glyph->top + 2 ) *
787 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
790 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
792 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
794 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
796 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
803 /* Outlining (find something better than nearest neighbour filtering ?) */
806 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
807 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
808 uint8_t left, current;
810 for( y = 1; y < (int)fmt.i_height - 1; y++ )
812 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
813 p_dst += p_region->p_picture->Y_PITCH;
816 for( x = 1; x < (int)fmt.i_width - 1; x++ )
819 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
820 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;
824 memset( p_top, 0, fmt.i_width );
830 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
831 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
832 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
833 int i_glyph_tmax, int i_align_offset,
834 uint8_t i_y, uint8_t i_u, uint8_t i_v,
835 subpicture_region_t *p_region)
839 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
841 p_dst_y = p_region->p_picture->Y_PIXELS;
842 p_dst_u = p_region->p_picture->U_PIXELS;
843 p_dst_v = p_region->p_picture->V_PIXELS;
844 p_dst_a = p_region->p_picture->A_PIXELS;
845 i_pitch = p_region->p_picture->A_PITCH;
847 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
848 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
850 for( y = 0; y < i_line_thickness; y++ )
852 int i_extra = p_this_glyph->bitmap.width;
856 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
857 (p_this_glyph_pos->x + p_this_glyph->left);
859 for( x = 0; x < i_extra; x++ )
863 /* break the underline around the tails of any glyphs which cross it */
864 for( z = x - i_line_thickness;
865 z < x + i_line_thickness && b_ok;
868 if( p_next_glyph && ( z >= i_extra ) )
870 int i_row = i_line_offset + p_next_glyph->top + y;
872 if( ( p_next_glyph->bitmap.rows > i_row ) &&
873 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
878 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
880 int i_row = i_line_offset + p_this_glyph->top + y;
882 if( ( p_this_glyph->bitmap.rows > i_row ) &&
883 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
892 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
893 p_dst_u[i_offset+x] = i_u;
894 p_dst_v[i_offset+x] = i_v;
895 p_dst_a[i_offset+x] = 255;
902 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
904 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
905 int i_pitch = p_region->p_picture->A_PITCH;
908 for( ; p_line != NULL; p_line = p_line->p_next )
910 int i_glyph_tmax=0, i = 0;
911 int i_bitmap_offset, i_offset, i_align_offset = 0;
912 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
914 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
915 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
918 if( p_line->i_width < i_width )
920 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
922 i_align_offset = i_width - p_line->i_width;
924 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
926 i_align_offset = ( i_width - p_line->i_width ) / 2;
930 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
932 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
934 i_offset = ( p_line->p_glyph_pos[ i ].y +
935 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
936 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
937 i_align_offset +xoffset;
939 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
941 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
943 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
944 if( p_dst[i_offset+x] <
945 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
947 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
956 /*****************************************************************************
957 * Render: place string in picture
958 *****************************************************************************
959 * This function merges the previously rendered freetype glyphs into a picture
960 *****************************************************************************/
961 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
962 line_desc_t *p_line, int i_width, int i_height )
964 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
966 int i, x, y, i_pitch, i_alpha;
967 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
969 if( i_width == 0 || i_height == 0 )
972 /* Create a new subpicture region */
973 memset( &fmt, 0, sizeof(video_format_t) );
974 fmt.i_chroma = VLC_CODEC_YUVA;
976 fmt.i_width = fmt.i_visible_width = i_width + 6;
977 fmt.i_height = fmt.i_visible_height = i_height + 6;
978 if( p_region->fmt.i_visible_width > 0 )
979 fmt.i_visible_width = p_region->fmt.i_visible_width;
980 if( p_region->fmt.i_visible_height > 0 )
981 fmt.i_visible_height = p_region->fmt.i_visible_height;
982 fmt.i_x_offset = fmt.i_y_offset = 0;
984 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
985 if( !p_region->p_picture )
989 /* Calculate text color components */
990 YUVFromRGB( (p_line->i_red << 16) |
991 (p_line->i_green << 8) |
994 i_alpha = p_line->i_alpha;
996 p_dst_y = p_region->p_picture->Y_PIXELS;
997 p_dst_u = p_region->p_picture->U_PIXELS;
998 p_dst_v = p_region->p_picture->V_PIXELS;
999 p_dst_a = p_region->p_picture->A_PIXELS;
1000 i_pitch = p_region->p_picture->A_PITCH;
1002 /* Initialize the region pixels */
1003 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
1005 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
1006 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1007 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1008 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1012 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1013 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1014 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1015 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1017 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1018 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1020 DrawBlack( p_line, i_width, p_region, 0, 0);
1021 DrawBlack( p_line, i_width, p_region, -1, 0);
1022 DrawBlack( p_line, i_width, p_region, 0, -1);
1023 DrawBlack( p_line, i_width, p_region, 1, 0);
1024 DrawBlack( p_line, i_width, p_region, 0, 1);
1027 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1029 DrawBlack( p_line, i_width, p_region, -1, -1);
1030 DrawBlack( p_line, i_width, p_region, -1, 1);
1031 DrawBlack( p_line, i_width, p_region, 1, -1);
1032 DrawBlack( p_line, i_width, p_region, 1, 1);
1034 DrawBlack( p_line, i_width, p_region, -2, 0);
1035 DrawBlack( p_line, i_width, p_region, 0, -2);
1036 DrawBlack( p_line, i_width, p_region, 2, 0);
1037 DrawBlack( p_line, i_width, p_region, 0, 2);
1039 DrawBlack( p_line, i_width, p_region, -2, -2);
1040 DrawBlack( p_line, i_width, p_region, -2, 2);
1041 DrawBlack( p_line, i_width, p_region, 2, -2);
1042 DrawBlack( p_line, i_width, p_region, 2, 2);
1044 DrawBlack( p_line, i_width, p_region, -3, 0);
1045 DrawBlack( p_line, i_width, p_region, 0, -3);
1046 DrawBlack( p_line, i_width, p_region, 3, 0);
1047 DrawBlack( p_line, i_width, p_region, 0, 3);
1050 for( ; p_line != NULL; p_line = p_line->p_next )
1052 int i_glyph_tmax = 0;
1053 int i_bitmap_offset, i_offset, i_align_offset = 0;
1054 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1056 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1057 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1060 if( p_line->i_width < i_width )
1062 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1064 i_align_offset = i_width - p_line->i_width;
1066 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1068 i_align_offset = ( i_width - p_line->i_width ) / 2;
1072 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1074 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1076 i_offset = ( p_line->p_glyph_pos[ i ].y +
1077 i_glyph_tmax - p_glyph->top + 3 ) *
1078 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1081 if( p_line->b_new_color_mode )
1083 /* Every glyph can (and in fact must) have its own color */
1084 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1087 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1089 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1091 uint8_t i_y_local = i_y;
1092 uint8_t i_u_local = i_u;
1093 uint8_t i_v_local = i_v;
1095 if( p_line->p_fg_bg_ratio != 0x00 )
1097 int i_split = p_glyph->bitmap.width *
1098 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1102 YUVFromRGB( p_line->p_bg_rgb[ i ],
1103 &i_y_local, &i_u_local, &i_v_local );
1107 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1109 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1110 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1112 p_dst_u[i_offset+x] = i_u;
1113 p_dst_v[i_offset+x] = i_v;
1115 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1116 p_dst_a[i_offset+x] = 0xff;
1119 i_offset += i_pitch;
1122 if( p_line->pi_underline_thickness[ i ] )
1124 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1125 p_line->pi_underline_offset[ i ],
1126 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1127 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1128 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1129 i_glyph_tmax, i_align_offset,
1136 /* Apply the alpha setting */
1137 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1138 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1144 * This function renders a text subpicture region into another one.
1145 * It also calculates the size needed for this string, and renders the
1146 * needed glyphs into memory. It is used as pf_add_string callback in
1147 * the vout method by this module
1149 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1150 subpicture_region_t *p_region_in )
1152 filter_sys_t *p_sys = p_filter->p_sys;
1153 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1154 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1155 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1156 int i_string_length;
1158 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1159 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1169 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1170 psz_string = p_region_in->psz_text;
1171 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1173 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1174 i_scale = val.i_int;
1176 if( p_region_in->p_style )
1178 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1179 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1180 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1184 i_font_color = p_sys->i_font_color;
1185 i_font_alpha = 255 - p_sys->i_font_opacity;
1186 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1189 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1190 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1191 SetFontSize( p_filter, i_font_size );
1193 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1194 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1195 i_blue = i_font_color & 0x000000FF;
1197 result.x = result.y = 0;
1198 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1200 psz_unicode = psz_unicode_orig =
1201 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1202 if( psz_unicode == NULL )
1204 #if defined(WORDS_BIGENDIAN)
1205 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1207 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1209 if( iconv_handle == (vlc_iconv_t)-1 )
1211 msg_Warn( p_filter, "unable to do conversion" );
1217 const char *p_in_buffer = psz_string;
1218 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1219 i_in_bytes = strlen( psz_string );
1220 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1221 i_out_bytes_left = i_out_bytes;
1222 p_out_buffer = (char *)psz_unicode;
1223 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1225 &p_out_buffer, &i_out_bytes_left );
1227 vlc_iconv_close( iconv_handle );
1231 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1232 "bytes left %u", (unsigned)i_in_bytes );
1235 *(uint32_t*)p_out_buffer = 0;
1236 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1239 #if defined(HAVE_FRIBIDI)
1241 uint32_t *p_fribidi_string;
1242 int32_t start_pos, pos = 0;
1244 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1245 if( !p_fribidi_string )
1248 /* Do bidi conversion line-by-line */
1249 while( pos < i_string_length )
1251 while( pos < i_string_length )
1253 i_char = psz_unicode[pos];
1254 if (i_char != '\r' && i_char != '\n')
1256 p_fribidi_string[pos] = i_char;
1260 while( pos < i_string_length )
1262 i_char = psz_unicode[pos];
1263 if (i_char == '\r' || i_char == '\n')
1267 if (pos > start_pos)
1269 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1270 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1273 (FriBidiChar*)p_fribidi_string + start_pos,
1278 free( psz_unicode_orig );
1279 psz_unicode = psz_unicode_orig = p_fribidi_string;
1280 p_fribidi_string[ i_string_length ] = 0;
1284 /* Calculate relative glyph positions and a bounding box for the
1286 if( !(p_line = NewLine( strlen( psz_string ))) )
1289 i_pen_x = i_pen_y = 0;
1291 psz_line_start = psz_unicode;
1293 #define face p_sys->p_face
1294 #define glyph face->glyph
1296 while( *psz_unicode )
1298 i_char = *psz_unicode++;
1299 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1304 if( i_char == '\n' )
1306 psz_line_start = psz_unicode;
1307 if( !(p_next = NewLine( strlen( psz_string ))) )
1309 p_line->p_next = p_next;
1310 p_line->i_width = line.xMax;
1311 p_line->i_height = face->size->metrics.height >> 6;
1312 p_line->pp_glyphs[ i ] = NULL;
1313 p_line->i_alpha = i_font_alpha;
1314 p_line->i_red = i_red;
1315 p_line->i_green = i_green;
1316 p_line->i_blue = i_blue;
1319 result.x = __MAX( result.x, line.xMax );
1320 result.y += face->size->metrics.height >> 6;
1323 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1324 i_pen_y += face->size->metrics.height >> 6;
1326 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1331 i_glyph_index = FT_Get_Char_Index( face, i_char );
1332 if( p_sys->i_use_kerning && i_glyph_index
1336 FT_Get_Kerning( face, i_previous, i_glyph_index,
1337 ft_kerning_default, &delta );
1338 i_pen_x += delta.x >> 6;
1341 p_line->p_glyph_pos[ i ].x = i_pen_x;
1342 p_line->p_glyph_pos[ i ].y = i_pen_y;
1343 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1346 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1350 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1353 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1357 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1358 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1361 FT_Done_Glyph( tmp_glyph );
1364 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1367 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1368 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1369 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1371 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1372 p_line->pp_glyphs[ i ] = NULL;
1374 p_line = NewLine( strlen( psz_string ));
1375 if( p_prev ) p_prev->p_next = p_line;
1376 else p_lines = p_line;
1378 uint32_t *psz_unicode_saved = psz_unicode;
1379 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1383 if( psz_unicode == psz_line_start )
1384 { /* try harder to break that line */
1385 psz_unicode = psz_unicode_saved;
1386 while( psz_unicode > psz_line_start &&
1387 *psz_unicode != '_' && *psz_unicode != '/' &&
1388 *psz_unicode != '\\' && *psz_unicode != '.' )
1393 if( psz_unicode == psz_line_start )
1395 msg_Warn( p_filter, "unbreakable string" );
1400 *psz_unicode = '\n';
1402 psz_unicode = psz_line_start;
1405 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1408 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1409 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1411 i_previous = i_glyph_index;
1412 i_pen_x += glyph->advance.x >> 6;
1416 p_line->i_width = line.xMax;
1417 p_line->i_height = face->size->metrics.height >> 6;
1418 p_line->pp_glyphs[ i ] = NULL;
1419 p_line->i_alpha = i_font_alpha;
1420 p_line->i_red = i_red;
1421 p_line->i_green = i_green;
1422 p_line->i_blue = i_blue;
1423 result.x = __MAX( result.x, line.xMax );
1424 result.y += line.yMax - line.yMin;
1429 p_region_out->i_x = p_region_in->i_x;
1430 p_region_out->i_y = p_region_in->i_y;
1432 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1433 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1435 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1437 free( psz_unicode_orig );
1438 FreeLines( p_lines );
1442 free( psz_unicode_orig );
1443 FreeLines( p_lines );
1444 return VLC_EGENERIC;
1447 #ifdef HAVE_FONTCONFIG
1448 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1449 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1450 bool b_italic, bool b_uline )
1452 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1456 p_style->i_font_size = i_font_size;
1457 p_style->i_font_color = i_font_color;
1458 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1459 p_style->b_italic = b_italic;
1460 p_style->b_bold = b_bold;
1461 p_style->b_underline = b_uline;
1463 p_style->psz_fontname = strdup( psz_fontname );
1468 static void DeleteStyle( ft_style_t *p_style )
1472 free( p_style->psz_fontname );
1477 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1484 if(( s1->i_font_size == s2->i_font_size ) &&
1485 ( s1->i_font_color == s2->i_font_color ) &&
1486 ( s1->b_italic == s2->b_italic ) &&
1487 ( s1->b_bold == s2->b_bold ) &&
1488 ( s1->b_underline == s2->b_underline ) &&
1489 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1496 static void IconvText( filter_t *p_filter, const char *psz_string,
1497 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1499 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1501 /* If memory hasn't been allocated for our output string, allocate it here
1502 * - the calling function must now be responsible for freeing it.
1504 if( !*ppsz_unicode )
1505 *ppsz_unicode = (uint32_t *)
1506 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1508 /* We don't need to handle a NULL pointer in *ppsz_unicode
1509 * if we are instead testing for a non NULL value like we are here */
1513 #if defined(WORDS_BIGENDIAN)
1514 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1516 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1518 if( iconv_handle != (vlc_iconv_t)-1 )
1520 char *p_in_buffer, *p_out_buffer;
1521 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1522 i_in_bytes = strlen( psz_string );
1523 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1524 i_out_bytes_left = i_out_bytes;
1525 p_in_buffer = (char *) psz_string;
1526 p_out_buffer = (char *) *ppsz_unicode;
1527 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1528 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1530 vlc_iconv_close( iconv_handle );
1534 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1535 "bytes left %u", (unsigned)i_in_bytes );
1539 *(uint32_t*)p_out_buffer = 0;
1541 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1546 msg_Warn( p_filter, "unable to do conversion" );
1551 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1552 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1555 ft_style_t *p_style = NULL;
1557 char *psz_fontname = NULL;
1558 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1559 uint32_t i_karaoke_bg_color = i_font_color;
1560 int i_font_size = p_sys->i_font_size;
1562 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1563 &i_font_color, &i_karaoke_bg_color ))
1565 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1566 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1571 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1572 bool b_uline, int i_karaoke_bgcolor,
1573 line_desc_t *p_line, uint32_t *psz_unicode,
1574 int *pi_pen_x, int i_pen_y, int *pi_start,
1575 FT_Vector *p_result )
1580 bool b_first_on_line = true;
1583 int i_pen_x_start = *pi_pen_x;
1585 uint32_t *psz_unicode_start = psz_unicode;
1587 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1589 /* Account for part of line already in position */
1590 for( i=0; i<*pi_start; i++ )
1594 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1595 ft_glyph_bbox_pixels, &glyph_size );
1597 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1598 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1599 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1600 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1606 b_first_on_line = false;
1608 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1614 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1615 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1619 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1620 ft_kerning_default, &delta );
1621 *pi_pen_x += delta.x >> 6;
1623 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1624 p_line->p_glyph_pos[ i ].y = i_pen_y;
1626 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1630 "unable to render text FT_Load_Glyph returned %d", i_error );
1631 p_line->pp_glyphs[ i ] = NULL;
1632 return VLC_EGENERIC;
1634 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1638 "unable to render text FT_Get_Glyph returned %d", i_error );
1639 p_line->pp_glyphs[ i ] = NULL;
1640 return VLC_EGENERIC;
1642 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1643 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1646 FT_Done_Glyph( tmp_glyph );
1651 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1652 p_face->size->metrics.y_scale));
1653 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1654 p_face->size->metrics.y_scale));
1656 p_line->pi_underline_offset[ i ] =
1657 ( aOffset < 0 ) ? -aOffset : aOffset;
1658 p_line->pi_underline_thickness[ i ] =
1659 ( aSize < 0 ) ? -aSize : aSize;
1661 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1662 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1663 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1664 p_line->p_fg_bg_ratio[ i ] = 0x00;
1666 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1667 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1668 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1670 for( ; i >= *pi_start; i-- )
1671 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1674 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1678 if( psz_unicode == psz_unicode_start )
1680 if( b_first_on_line )
1682 msg_Warn( p_filter, "unbreakable string" );
1683 p_line->pp_glyphs[ i ] = NULL;
1684 return VLC_EGENERIC;
1686 *pi_pen_x = i_pen_x_start;
1688 p_line->i_width = line.xMax;
1689 p_line->i_height = __MAX( p_line->i_height,
1690 p_face->size->metrics.height >> 6 );
1691 p_line->pp_glyphs[ i ] = NULL;
1693 p_result->x = __MAX( p_result->x, line.xMax );
1694 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1695 i_yMax - i_yMin ) );
1700 *psz_unicode = '\n';
1702 psz_unicode = psz_unicode_start;
1703 *pi_pen_x = i_pen_x_start;
1711 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1712 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1714 i_previous = i_glyph_index;
1715 *pi_pen_x += p_face->glyph->advance.x >> 6;
1718 p_line->i_width = line.xMax;
1719 p_line->i_height = __MAX( p_line->i_height,
1720 p_face->size->metrics.height >> 6 );
1721 p_line->pp_glyphs[ i ] = NULL;
1723 p_result->x = __MAX( p_result->x, line.xMax );
1724 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1725 line.yMax - line.yMin ) );
1729 /* Get rid of any text processed - if necessary repositioning
1730 * at the start of a new line of text
1734 *psz_unicode_start = '\0';
1736 else if( psz_unicode > psz_unicode_start )
1738 for( i=0; psz_unicode[ i ]; i++ )
1739 psz_unicode_start[ i ] = psz_unicode[ i ];
1740 psz_unicode_start[ i ] = '\0';
1746 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1747 uint32_t **psz_text_out, uint32_t *pi_runs,
1748 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1749 ft_style_t *p_style )
1751 uint32_t i_string_length = 0;
1753 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1754 *psz_text_out += i_string_length;
1756 if( ppp_styles && ppi_run_lengths )
1762 *ppp_styles = (ft_style_t **)
1763 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1765 else if( *pi_runs == 1 )
1767 *ppp_styles = (ft_style_t **)
1768 malloc( *pi_runs * sizeof( ft_style_t * ) );
1771 /* We have just malloc'ed this memory successfully -
1772 * *pi_runs HAS to be within the memory area of *ppp_styles */
1775 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1779 if( *ppi_run_lengths )
1781 *ppi_run_lengths = (uint32_t *)
1782 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1784 else if( *pi_runs == 1 )
1786 *ppi_run_lengths = (uint32_t *)
1787 malloc( *pi_runs * sizeof( uint32_t ) );
1790 /* same remarks here */
1791 if( *ppi_run_lengths )
1793 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1796 /* If we couldn't use the p_style argument due to memory allocation
1797 * problems above, release it here.
1799 if( p_style ) DeleteStyle( p_style );
1802 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1806 for( k=0; k < p_sys->i_font_attachments; k++ )
1808 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1810 FT_Face p_face = NULL;
1812 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1820 bool match = !strcasecmp( p_face->family_name,
1821 p_style->psz_fontname );
1823 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1824 match = match && p_style->b_bold;
1826 match = match && !p_style->b_bold;
1828 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1829 match = match && p_style->b_italic;
1831 match = match && !p_style->b_italic;
1839 FT_Done_Face( p_face );
1844 return VLC_EGENERIC;
1847 static int ProcessLines( filter_t *p_filter,
1852 uint32_t *pi_run_lengths,
1853 ft_style_t **pp_styles,
1854 line_desc_t **pp_lines,
1856 FT_Vector *p_result,
1860 uint32_t *pi_k_run_lengths,
1861 uint32_t *pi_k_durations )
1863 filter_sys_t *p_sys = p_filter->p_sys;
1864 ft_style_t **pp_char_styles;
1865 int *p_new_positions = NULL;
1866 int8_t *p_levels = NULL;
1867 uint8_t *pi_karaoke_bar = NULL;
1871 /* Assign each character in the text string its style explicitly, so that
1872 * after the characters have been shuffled around by Fribidi, we can re-apply
1873 * the styles, and to simplify the calculation of runs within a line.
1875 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1876 if( !pp_char_styles )
1881 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1882 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1883 * we just won't be able to display the progress bar; at least we'll
1889 for( j = 0; j < i_runs; j++ )
1890 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1891 pp_char_styles[ i++ ] = pp_styles[ j ];
1893 #if defined(HAVE_FRIBIDI)
1895 ft_style_t **pp_char_styles_new;
1896 int *p_old_positions;
1897 uint32_t *p_fribidi_string;
1898 int start_pos, pos = 0;
1900 pp_char_styles_new = (ft_style_t **)
1901 malloc( i_len * sizeof( ft_style_t * ));
1903 p_fribidi_string = (uint32_t *)
1904 malloc( (i_len + 1) * sizeof(uint32_t) );
1905 p_old_positions = (int *)
1906 malloc( (i_len + 1) * sizeof( int ) );
1907 p_new_positions = (int *)
1908 malloc( (i_len + 1) * sizeof( int ) );
1909 p_levels = (int8_t *)
1910 malloc( (i_len + 1) * sizeof( int8_t ) );
1912 if( ! pp_char_styles_new ||
1913 ! p_fribidi_string ||
1914 ! p_old_positions ||
1915 ! p_new_positions ||
1919 free( p_old_positions );
1920 free( p_new_positions );
1921 free( p_fribidi_string );
1922 free( pp_char_styles_new );
1923 free( pi_karaoke_bar );
1925 free( pp_char_styles );
1929 /* Do bidi conversion line-by-line */
1932 while(pos < i_len) {
1933 if (psz_text[pos] != '\n')
1935 p_fribidi_string[pos] = psz_text[pos];
1936 pp_char_styles_new[pos] = pp_char_styles[pos];
1937 p_new_positions[pos] = pos;
1942 while(pos < i_len) {
1943 if (psz_text[pos] == '\n')
1947 if (pos > start_pos)
1949 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1950 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1951 pos - start_pos, &base_dir,
1952 (FriBidiChar*)p_fribidi_string + start_pos,
1953 p_new_positions + start_pos,
1955 p_levels + start_pos );
1956 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1958 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1959 p_old_positions[ j - start_pos ] ];
1960 p_new_positions[ j ] += start_pos;
1964 free( p_old_positions );
1965 free( pp_char_styles );
1966 pp_char_styles = pp_char_styles_new;
1967 psz_text = p_fribidi_string;
1968 p_fribidi_string[ i_len ] = 0;
1971 /* Work out the karaoke */
1972 if( pi_karaoke_bar )
1974 int64_t i_last_duration = 0;
1975 int64_t i_duration = 0;
1976 int64_t i_start_pos = 0;
1977 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1979 for( k = 0; k< i_k_runs; k++ )
1981 double fraction = 0.0;
1983 i_duration += pi_k_durations[ k ];
1985 if( i_duration < i_elapsed )
1987 /* Completely finished this run-length -
1988 * let it render normally */
1992 else if( i_elapsed < i_last_duration )
1994 /* Haven't got up to this segment yet -
1995 * render it completely in karaoke BG mode */
2001 /* Partway through this run */
2003 fraction = (double)(i_elapsed - i_last_duration) /
2004 (double)pi_k_durations[ k ];
2006 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2008 double shade = pi_k_run_lengths[ k ] * fraction;
2010 if( p_new_positions )
2011 j = p_new_positions[ i_start_pos + i ];
2013 j = i_start_pos + i;
2015 if( i < (uint32_t)shade )
2016 pi_karaoke_bar[ j ] = 0xff;
2017 else if( (double)i > shade )
2018 pi_karaoke_bar[ j ] = 0x00;
2021 shade -= (int)shade;
2022 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2023 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2027 i_last_duration = i_duration;
2028 i_start_pos += pi_k_run_lengths[ k ];
2032 free( p_new_positions );
2034 FT_Vector tmp_result;
2036 line_desc_t *p_line = NULL;
2037 line_desc_t *p_prev = NULL;
2043 p_result->x = p_result->y = 0;
2044 tmp_result.x = tmp_result.y = 0;
2047 for( k = 0; k <= (uint32_t) i_len; k++ )
2049 if( ( k == (uint32_t) i_len ) ||
2051 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2053 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2055 /* End of the current style run */
2056 FT_Face p_face = NULL;
2059 /* Look for a match amongst our attachments first */
2060 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2064 char *psz_fontfile = NULL;
2066 vlc_mutex_lock( &fb_lock );
2067 if( p_sys->b_fontconfig_ok )
2069 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2070 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2071 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2072 p_style->psz_fontname,
2077 vlc_mutex_unlock( &fb_lock );
2079 if( psz_fontfile && ! *psz_fontfile )
2081 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2082 " so using default font", p_style->psz_fontname,
2083 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2084 (p_style->b_bold ? "(Bold)" :
2085 (p_style->b_italic ? "(Italic)" : ""))) );
2086 free( psz_fontfile );
2087 psz_fontfile = NULL;
2092 if( FT_New_Face( p_sys->p_library,
2093 psz_fontfile, i_idx, &p_face ) )
2095 free( psz_fontfile );
2096 free( pp_char_styles );
2097 #if defined(HAVE_FRIBIDI)
2100 free( pi_karaoke_bar );
2101 return VLC_EGENERIC;
2103 free( psz_fontfile );
2107 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2109 /* We've loaded a font face which is unhelpful for actually
2110 * rendering text - fallback to the default one.
2112 FT_Done_Face( p_face );
2116 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2117 ft_encoding_unicode ) ||
2118 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2119 p_style->i_font_size ) )
2121 if( p_face ) FT_Done_Face( p_face );
2122 free( pp_char_styles );
2123 #if defined(HAVE_FRIBIDI)
2126 free( pi_karaoke_bar );
2127 return VLC_EGENERIC;
2129 p_sys->i_use_kerning =
2130 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2133 uint32_t *psz_unicode = (uint32_t *)
2134 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2137 if( p_face ) FT_Done_Face( p_face );
2138 free( pp_char_styles );
2139 free( psz_unicode );
2140 #if defined(HAVE_FRIBIDI)
2143 free( pi_karaoke_bar );
2146 memcpy( psz_unicode, psz_text + i_prev,
2147 sizeof( uint32_t ) * ( k - i_prev ) );
2148 psz_unicode[ k - i_prev ] = 0;
2149 while( *psz_unicode )
2153 if( !(p_line = NewLine( i_len - i_prev)) )
2155 if( p_face ) FT_Done_Face( p_face );
2156 free( pp_char_styles );
2157 free( psz_unicode );
2158 #if defined(HAVE_FRIBIDI)
2161 free( pi_karaoke_bar );
2164 /* New Color mode only works in YUVA rendering mode --
2165 * (RGB mode has palette constraints on it). We therefore
2166 * need to populate the legacy colour fields also.
2168 p_line->b_new_color_mode = true;
2169 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2170 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2171 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2172 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2173 p_line->p_next = NULL;
2175 i_pen_y += tmp_result.y;
2179 if( p_prev ) p_prev->p_next = p_line;
2180 else *pp_lines = p_line;
2183 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2184 p_style->i_font_color, p_style->b_underline,
2185 p_style->i_karaoke_bg_color,
2186 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2187 &tmp_result ) != VLC_SUCCESS )
2189 if( p_face ) FT_Done_Face( p_face );
2190 free( pp_char_styles );
2191 free( psz_unicode );
2192 #if defined(HAVE_FRIBIDI)
2195 free( pi_karaoke_bar );
2196 return VLC_EGENERIC;
2201 p_result->x = __MAX( p_result->x, tmp_result.x );
2202 p_result->y += tmp_result.y;
2207 if( *psz_unicode == '\n')
2211 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2213 *c_ptr = *(c_ptr+1);
2218 free( psz_unicode );
2219 if( p_face ) FT_Done_Face( p_face );
2223 free( pp_char_styles );
2224 #if defined(HAVE_FRIBIDI)
2229 p_result->x = __MAX( p_result->x, tmp_result.x );
2230 p_result->y += tmp_result.y;
2233 if( pi_karaoke_bar )
2236 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2238 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2240 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2244 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2246 /* 100% BG colour will render faster if we
2247 * instead make it 100% FG colour, so leave
2248 * the ratio alone and copy the value across
2250 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2254 if( pi_karaoke_bar[ i ] & 0x80 )
2256 /* Swap Left and Right sides over for Right aligned
2257 * language text (eg. Arabic, Hebrew)
2259 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2261 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2262 p_line->p_bg_rgb[ k ] = i_tmp;
2264 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2267 /* Jump over the '\n' at the line-end */
2270 free( pi_karaoke_bar );
2276 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2277 subpicture_region_t *p_region_in )
2279 int rv = VLC_SUCCESS;
2280 stream_t *p_sub = NULL;
2281 xml_t *p_xml = NULL;
2282 xml_reader_t *p_xml_reader = NULL;
2284 if( !p_region_in || !p_region_in->psz_html )
2285 return VLC_EGENERIC;
2287 /* Reset the default fontsize in case screen metrics have changed */
2288 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2290 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2291 (uint8_t *) p_region_in->psz_html,
2292 strlen( p_region_in->psz_html ),
2296 p_xml = xml_Create( p_filter );
2299 bool b_karaoke = false;
2301 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2304 /* Look for Root Node */
2305 if( xml_ReaderRead( p_xml_reader ) == 1 )
2307 char *psz_node = xml_ReaderName( p_xml_reader );
2309 if( !strcasecmp( "karaoke", psz_node ) )
2311 /* We're going to have to render the text a number
2312 * of times to show the progress marker on the text.
2314 var_SetBool( p_filter, "text-rerender", true );
2317 else if( !strcasecmp( "text", psz_node ) )
2323 /* Only text and karaoke tags are supported */
2324 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2325 xml_ReaderDelete( p_xml, p_xml_reader );
2326 p_xml_reader = NULL;
2338 uint32_t i_runs = 0;
2339 uint32_t i_k_runs = 0;
2340 uint32_t *pi_run_lengths = NULL;
2341 uint32_t *pi_k_run_lengths = NULL;
2342 uint32_t *pi_k_durations = NULL;
2343 ft_style_t **pp_styles = NULL;
2345 line_desc_t *p_lines = NULL;
2347 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2348 sizeof( uint32_t ) );
2353 rv = ProcessNodes( p_filter, p_xml_reader,
2354 p_region_in->p_style, psz_text, &i_len,
2355 &i_runs, &pi_run_lengths, &pp_styles,
2357 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2360 p_region_out->i_x = p_region_in->i_x;
2361 p_region_out->i_y = p_region_in->i_y;
2363 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2365 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2366 pi_run_lengths, pp_styles, &p_lines, &result,
2367 b_karaoke, i_k_runs, pi_k_run_lengths,
2371 for( k=0; k<i_runs; k++)
2372 DeleteStyle( pp_styles[k] );
2374 free( pi_run_lengths );
2377 /* Don't attempt to render text that couldn't be layed out
2380 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2382 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2384 Render( p_filter, p_region_out, p_lines,
2385 result.x, result.y );
2389 RenderYUVA( p_filter, p_region_out, p_lines,
2390 result.x, result.y );
2394 FreeLines( p_lines );
2396 xml_ReaderDelete( p_xml, p_xml_reader );
2398 xml_Delete( p_xml );
2400 stream_Delete( p_sub );
2406 static char* FontConfig_Select( FcConfig* priv, const char* family,
2407 bool b_bold, bool b_italic, int *i_idx )
2410 FcPattern *pat, *p_pat;
2414 pat = FcPatternCreate();
2415 if (!pat) return NULL;
2417 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2418 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2419 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2420 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2422 FcDefaultSubstitute( pat );
2424 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2426 FcPatternDestroy( pat );
2430 p_pat = FcFontMatch( priv, pat, &result );
2431 FcPatternDestroy( pat );
2432 if( !p_pat ) return NULL;
2434 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2435 || ( val_b != FcTrue ) )
2437 FcPatternDestroy( p_pat );
2440 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2445 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2447 FcPatternDestroy( p_pat );
2452 if( strcasecmp((const char*)val_s, family ) != 0 )
2453 msg_Warn( p_filter, "fontconfig: selected font family is not"
2454 "the requested one: '%s' != '%s'\n",
2455 (const char*)val_s, family );
2458 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2460 FcPatternDestroy( p_pat );
2464 FcPatternDestroy( p_pat );
2465 return strdup( (const char*)val_s );
2469 static void FreeLine( line_desc_t *p_line )
2472 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2474 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2476 free( p_line->pp_glyphs );
2477 free( p_line->p_glyph_pos );
2478 free( p_line->p_fg_rgb );
2479 free( p_line->p_bg_rgb );
2480 free( p_line->p_fg_bg_ratio );
2481 free( p_line->pi_underline_offset );
2482 free( p_line->pi_underline_thickness );
2486 static void FreeLines( line_desc_t *p_lines )
2488 line_desc_t *p_line, *p_next;
2490 if( !p_lines ) return;
2492 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2494 p_next = p_line->p_next;
2499 static line_desc_t *NewLine( int i_count )
2501 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2503 if( !p_line ) return NULL;
2504 p_line->i_height = 0;
2505 p_line->i_width = 0;
2506 p_line->p_next = NULL;
2508 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2509 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2510 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2511 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2512 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2513 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2514 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2515 if( ( p_line->pp_glyphs == NULL ) ||
2516 ( p_line->p_glyph_pos == NULL ) ||
2517 ( p_line->p_fg_rgb == NULL ) ||
2518 ( p_line->p_bg_rgb == NULL ) ||
2519 ( p_line->p_fg_bg_ratio == NULL ) ||
2520 ( p_line->pi_underline_offset == NULL ) ||
2521 ( p_line->pi_underline_thickness == NULL ) )
2523 free( p_line->pi_underline_thickness );
2524 free( p_line->pi_underline_offset );
2525 free( p_line->p_fg_rgb );
2526 free( p_line->p_bg_rgb );
2527 free( p_line->p_fg_bg_ratio );
2528 free( p_line->p_glyph_pos );
2529 free( p_line->pp_glyphs );
2533 p_line->pp_glyphs[0] = NULL;
2534 p_line->b_new_color_mode = false;
2539 static int GetFontSize( filter_t *p_filter )
2541 filter_sys_t *p_sys = p_filter->p_sys;
2545 if( p_sys->i_default_font_size )
2547 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2548 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2550 i_size = p_sys->i_default_font_size;
2554 var_Get( p_filter, "freetype-rel-fontsize", &val );
2557 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2558 p_filter->p_sys->i_display_height =
2559 p_filter->fmt_out.video.i_height;
2564 msg_Warn( p_filter, "invalid fontsize, using 12" );
2565 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2566 i_size = 12 * val.i_int / 1000;
2573 static int SetFontSize( filter_t *p_filter, int i_size )
2575 filter_sys_t *p_sys = p_filter->p_sys;
2579 i_size = GetFontSize( p_filter );
2581 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2584 p_sys->i_font_size = i_size;
2586 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2588 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2589 return VLC_EGENERIC;
2595 static void YUVFromRGB( uint32_t i_argb,
2596 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2598 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2599 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2600 int i_blue = ( i_argb & 0x000000ff );
2602 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2603 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2604 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2605 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2606 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2607 -585 * i_blue + 4096 + 1048576) >> 13, 240);