1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
47 #include FT_FREETYPE_H
49 #define FT_FLOOR(X) ((X & -64) >> 6)
50 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
51 #define FT_MulFix(v, s) (((v)*(s))>>16)
54 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
55 #define FC_DEFAULT_FONT "Lucida Grande"
56 #elif defined( SYS_BEOS )
57 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
58 #define FC_DEFAULT_FONT "Swiss"
59 #elif defined( WIN32 )
60 #define DEFAULT_FONT "" /* Default font found at run-time */
61 #define FC_DEFAULT_FONT "Arial"
63 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
64 #define FC_DEFAULT_FONT "Serif Bold"
67 #if defined(HAVE_FRIBIDI)
68 #include <fribidi/fribidi.h>
71 #ifdef HAVE_FONTCONFIG
72 #include <fontconfig/fontconfig.h>
74 #define DEFAULT_FONT FC_DEFAULT_FONT
79 /*****************************************************************************
81 *****************************************************************************/
82 static int Create ( vlc_object_t * );
83 static void Destroy( vlc_object_t * );
85 #define FONT_TEXT N_("Font")
87 #ifdef HAVE_FONTCONFIG
88 #define FONT_LONGTEXT N_("Font family for the font you want to use")
90 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
93 #define FONTSIZE_TEXT N_("Font size in pixels")
94 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
95 "that will be rendered on the video. " \
96 "If set to something different than 0 this option will override the " \
97 "relative font size." )
98 #define OPACITY_TEXT N_("Opacity")
99 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
100 "text that will be rendered on the video. 0 = transparent, " \
101 "255 = totally opaque. " )
102 #define COLOR_TEXT N_("Text default color")
103 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
104 "the video. This must be an hexadecimal (like HTML colors). The first two "\
105 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
106 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
107 #define FONTSIZER_TEXT N_("Relative font size")
108 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
109 "fonts that will be rendered on the video. If absolute font size is set, "\
110 "relative size will be overriden." )
112 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
113 static const char *const ppsz_sizes_text[] = {
114 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
115 #define YUVP_TEXT N_("Use YUVP renderer")
116 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
117 "This option is only needed if you want to encode into DVB subtitles" )
118 #define EFFECT_TEXT N_("Font Effect")
119 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
120 "text to improve its readability." )
122 #define EFFECT_BACKGROUND 1
123 #define EFFECT_OUTLINE 2
124 #define EFFECT_OUTLINE_FAT 3
126 static int const pi_effects[] = { 1, 2, 3 };
127 static const char *const ppsz_effects_text[] = {
128 N_("Background"),N_("Outline"), N_("Fat Outline") };
129 static const int pi_color_values[] = {
130 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
131 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
132 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
134 static const char *const ppsz_color_descriptions[] = {
135 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
136 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
137 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
140 set_shortname( N_("Text renderer"))
141 set_description( N_("Freetype2 font renderer") )
142 set_category( CAT_VIDEO )
143 set_subcategory( SUBCAT_VIDEO_SUBPIC )
145 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
148 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
149 FONTSIZE_LONGTEXT, true )
151 /* opacity valid on 0..255, with default 255 = fully opaque */
152 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
153 OPACITY_TEXT, OPACITY_LONGTEXT, true )
155 /* hook to the color values list, with default 0x00ffffff = white */
156 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
157 COLOR_LONGTEXT, false )
158 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
160 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
161 FONTSIZER_LONGTEXT, false )
162 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
163 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
164 EFFECT_LONGTEXT, false )
165 change_integer_list( pi_effects, ppsz_effects_text, NULL )
167 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
168 YUVP_LONGTEXT, true )
169 set_capability( "text renderer", 100 )
170 add_shortcut( "text" )
171 set_callbacks( Create, Destroy )
176 /*****************************************************************************
178 *****************************************************************************/
180 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
181 static int RenderText( filter_t *, subpicture_region_t *,
182 subpicture_region_t * );
183 #ifdef HAVE_FONTCONFIG
184 static int RenderHtml( filter_t *, subpicture_region_t *,
185 subpicture_region_t * );
186 static char *FontConfig_Select( FcConfig *, const char *,
191 static int LoadFontsFromAttachments( filter_t *p_filter );
193 static int GetFontSize( filter_t *p_filter );
194 static int SetFontSize( filter_t *, int );
195 static void YUVFromRGB( uint32_t i_argb,
196 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
198 typedef struct line_desc_t line_desc_t;
201 /** NULL-terminated list of glyphs making the string */
202 FT_BitmapGlyph *pp_glyphs;
203 /** list of relative positions for the glyphs */
204 FT_Vector *p_glyph_pos;
205 /** list of RGB information for styled text
206 * -- if the rendering mode supports it (RenderYUVA) and
207 * b_new_color_mode is set, then it becomes possible to
208 * have multicoloured text within the subtitles. */
211 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
212 bool b_new_color_mode;
213 /** underline information -- only supplied if text should be underlined */
214 uint16_t *pi_underline_offset;
215 uint16_t *pi_underline_thickness;
219 int i_red, i_green, i_blue;
224 static line_desc_t *NewLine( int );
229 uint32_t i_font_color; /* ARGB */
230 uint32_t i_karaoke_bg_color; /* ARGB */
237 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
238 static void FreeLines( line_desc_t * );
239 static void FreeLine( line_desc_t * );
241 #ifdef HAVE_FONTCONFIG
242 static vlc_object_t *FontBuilderAttach( filter_t *p_filter );
243 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
244 static void* FontBuilderThread( vlc_object_t *p_this);
245 static void FontBuilderDestructor( vlc_object_t *p_this );
246 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
247 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
251 /*****************************************************************************
252 * filter_sys_t: freetype local data
253 *****************************************************************************
254 * This structure is part of the video output thread descriptor.
255 * It describes the freetype specific properties of an output thread.
256 *****************************************************************************/
259 FT_Library p_library; /* handle to library */
260 FT_Face p_face; /* handle to face object */
262 uint8_t i_font_opacity;
267 int i_default_font_size;
268 int i_display_height;
269 #ifdef HAVE_FONTCONFIG
270 bool b_fontconfig_ok;
271 FcConfig *p_fontconfig;
274 input_attachment_t **pp_font_attachments;
275 int i_font_attachments;
277 vlc_object_t *p_fontbuilder;
280 #define UCHAR uint32_t
281 #define TR_DEFAULT_FONT FC_DEFAULT_FONT
282 #define TR_FONT_STYLE_PTR ft_style_t *
284 #include "text_renderer.h"
286 /*****************************************************************************
287 * Create: allocates osd-text video thread output method
288 *****************************************************************************
289 * This function allocates and initializes a Clone vout method.
290 *****************************************************************************/
291 static int Create( vlc_object_t *p_this )
293 filter_t *p_filter = (filter_t *)p_this;
295 char *psz_fontfile=NULL;
296 char *psz_fontfamily=NULL;
297 int i_error,fontindex;
299 #ifdef HAVE_FONTCONFIG
300 FcPattern *fontpattern = NULL, *fontmatch = NULL;
301 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 FcDefaultSubstitute( fontpattern );
365 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
366 if( !fontmatch || fontresult == FcResultNoMatch )
369 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
370 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
373 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
375 psz_fontfile = psz_fontfamily;
378 i_error = FT_Init_FreeType( &p_sys->p_library );
381 msg_Err( p_filter, "couldn't initialize freetype" );
385 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
386 fontindex, &p_sys->p_face );
388 if( i_error == FT_Err_Unknown_File_Format )
390 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
395 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
399 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
402 msg_Err( p_filter, "font has no unicode translation table" );
406 #ifdef HAVE_FONTCONFIG
407 p_sys->b_fontconfig_ok = false;
408 p_sys->p_fontconfig = NULL;
409 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
412 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
414 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
417 p_sys->pp_font_attachments = NULL;
418 p_sys->i_font_attachments = 0;
420 p_filter->pf_render_text = RenderText;
421 #ifdef HAVE_FONTCONFIG
422 p_filter->pf_render_html = RenderHtml;
423 FcPatternDestroy( fontmatch );
424 FcPatternDestroy( fontpattern );
426 p_filter->pf_render_html = NULL;
429 free( psz_fontfamily );
430 LoadFontsFromAttachments( p_filter );
435 #ifdef HAVE_FONTCONFIG
436 if( fontmatch ) FcPatternDestroy( fontmatch );
437 if( fontpattern ) FcPatternDestroy( fontpattern );
439 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
440 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
441 free( psz_fontfile );
446 /*****************************************************************************
447 * Destroy: destroy Clone video thread output method
448 *****************************************************************************
449 * Clean up all data and library connections
450 *****************************************************************************/
451 static void Destroy( vlc_object_t *p_this )
453 filter_t *p_filter = (filter_t *)p_this;
454 filter_sys_t *p_sys = p_filter->p_sys;
456 if( p_sys->pp_font_attachments )
460 for( k = 0; k < p_sys->i_font_attachments; k++ )
461 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
463 free( p_sys->pp_font_attachments );
466 #ifdef HAVE_FONTCONFIG
467 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
470 /* FcFini asserts calling the subfunction FcCacheFini()
471 * even if no other library functions have been made since FcInit(),
472 * so don't call it. */
474 FT_Done_Face( p_sys->p_face );
475 FT_Done_FreeType( p_sys->p_library );
479 #ifdef HAVE_FONTCONFIG
480 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
482 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
484 /* Check for an existing Fontbuilder thread */
485 vlc_mutex_lock( &fb_lock );
486 vlc_object_t *p_fontbuilder =
487 vlc_object_find_name( p_filter->p_libvlc,
488 "fontlist builder", FIND_CHILD );
492 /* Create the FontBuilderThread thread as a child of a top-level
493 * object, so that it can survive the destruction of the
494 * freetype object - the fontlist only needs to be built once,
495 * and calling the fontbuild a second time while the first is
496 * still in progress can cause thread instabilities.
498 * XXX The fontbuilder will be destroy as soon as it is unused.
501 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
502 sizeof(vlc_object_t) );
505 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
506 p_fontbuilder->p_private = NULL;
507 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
509 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
511 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
512 var_SetBool( p_fontbuilder, "build-done", false );
513 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
514 var_SetBool( p_fontbuilder, "build-joined", false );
516 if( vlc_thread_create( p_fontbuilder,
519 VLC_THREAD_PRIORITY_LOW ) )
521 msg_Warn( p_filter, "fontconfig database builder thread can't "
522 "be launched. Font styling support will be limited." );
529 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
530 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
532 vlc_mutex_unlock( &fb_lock );
533 return p_fontbuilder;
535 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
537 vlc_mutex_lock( &fb_lock );
540 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
542 /* We wait for the thread on the first FontBuilderDetach */
543 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
545 var_SetBool( p_fontbuilder, "build-joined", true );
546 vlc_mutex_unlock( &fb_lock );
547 /* We need to unlock otherwise we may not join (the thread waiting
548 * for the lock). It is safe to unlock as no one else will try a
549 * join and we have a reference on the object) */
550 vlc_thread_join( p_fontbuilder );
551 vlc_mutex_lock( &fb_lock );
553 vlc_object_release( p_fontbuilder );
555 vlc_mutex_unlock( &fb_lock );
557 static void* FontBuilderThread( vlc_object_t *p_this )
559 FcConfig *p_fontconfig = FcInitLoadConfig();
564 int canc = vlc_savecancel ();
566 //msg_Dbg( p_this, "Building font database..." );
567 msg_Dbg( p_this, "Building font database..." );
569 if(! FcConfigBuildFonts( p_fontconfig ))
571 /* Don't destroy the fontconfig object - we won't be able to do
572 * italics or bold or change the font face, but we will still
573 * be able to do underline and change the font size.
575 msg_Err( p_this, "fontconfig database can't be built. "
576 "Font styling won't be available" );
580 msg_Dbg( p_this, "Finished building font database." );
581 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
583 vlc_mutex_lock( &fb_lock );
584 p_this->p_private = p_fontconfig;
585 vlc_mutex_unlock( &fb_lock );
587 var_SetBool( p_this, "build-done", true );
588 vlc_restorecancel (canc);
592 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
594 filter_sys_t *p_sys = p_filter->p_sys;
596 p_sys->p_fontconfig = p_fontbuilder->p_private;
597 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
599 static void FontBuilderDestructor( vlc_object_t *p_this )
601 FcConfig *p_fontconfig = p_this->p_private;
604 FcConfigDestroy( p_fontconfig );
606 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
607 vlc_value_t oldval, vlc_value_t newval, void *param )
609 filter_t *p_filter = param;
613 vlc_mutex_lock( &fb_lock );
615 FontBuilderGetFcConfig( p_filter, p_this );
617 vlc_mutex_unlock( &fb_lock );
626 /*****************************************************************************
627 * Make any TTF/OTF fonts present in the attachments of the media file
628 * and store them for later use by the FreeType Engine
629 *****************************************************************************/
630 static int LoadFontsFromAttachments( filter_t *p_filter )
632 filter_sys_t *p_sys = p_filter->p_sys;
633 input_thread_t *p_input;
634 input_attachment_t **pp_attachments;
635 int i_attachments_cnt;
637 int rv = VLC_SUCCESS;
639 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
643 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
645 vlc_object_release(p_input);
649 p_sys->i_font_attachments = 0;
650 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
651 if(! p_sys->pp_font_attachments )
654 for( k = 0; k < i_attachments_cnt; k++ )
656 input_attachment_t *p_attach = pp_attachments[k];
658 if( p_sys->pp_font_attachments )
660 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
661 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
662 ( p_attach->i_data > 0 ) &&
663 ( p_attach->p_data != NULL ) )
665 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
669 vlc_input_attachment_Delete( p_attach );
674 vlc_input_attachment_Delete( p_attach );
677 free( pp_attachments );
679 vlc_object_release(p_input);
684 /*****************************************************************************
685 * Render: place string in picture
686 *****************************************************************************
687 * This function merges the previously rendered freetype glyphs into a picture
688 *****************************************************************************/
689 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
690 line_desc_t *p_line, int i_width, int i_height )
692 static const uint8_t pi_gamma[16] =
693 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
694 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
698 int i, x, y, i_pitch;
699 uint8_t i_y; /* YUV values, derived from incoming RGB */
702 /* Create a new subpicture region */
703 memset( &fmt, 0, sizeof(video_format_t) );
704 fmt.i_chroma = VLC_CODEC_YUVP;
706 fmt.i_width = fmt.i_visible_width = i_width + 4;
707 fmt.i_height = fmt.i_visible_height = i_height + 4;
708 if( p_region->fmt.i_visible_width > 0 )
709 fmt.i_visible_width = p_region->fmt.i_visible_width;
710 if( p_region->fmt.i_visible_height > 0 )
711 fmt.i_visible_height = p_region->fmt.i_visible_height;
712 fmt.i_x_offset = fmt.i_y_offset = 0;
714 assert( !p_region->p_picture );
715 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
716 if( !p_region->p_picture )
720 /* Calculate text color components */
721 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
722 25 * p_line->i_blue + 128) >> 8) + 16;
723 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
724 112 * p_line->i_blue + 128) >> 8) + 128;
725 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
726 18 * p_line->i_blue + 128) >> 8) + 128;
729 fmt.p_palette->i_entries = 16;
730 for( i = 0; i < 8; i++ )
732 fmt.p_palette->palette[i][0] = 0;
733 fmt.p_palette->palette[i][1] = 0x80;
734 fmt.p_palette->palette[i][2] = 0x80;
735 fmt.p_palette->palette[i][3] = pi_gamma[i];
736 fmt.p_palette->palette[i][3] =
737 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
739 for( i = 8; i < fmt.p_palette->i_entries; i++ )
741 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
742 fmt.p_palette->palette[i][1] = i_u;
743 fmt.p_palette->palette[i][2] = i_v;
744 fmt.p_palette->palette[i][3] = pi_gamma[i];
745 fmt.p_palette->palette[i][3] =
746 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
749 p_dst = p_region->p_picture->Y_PIXELS;
750 i_pitch = p_region->p_picture->Y_PITCH;
752 /* Initialize the region pixels */
753 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
755 for( ; p_line != NULL; p_line = p_line->p_next )
757 int i_glyph_tmax = 0;
758 int i_bitmap_offset, i_offset, i_align_offset = 0;
759 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
761 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
762 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
765 if( p_line->i_width < i_width )
767 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
769 i_align_offset = i_width - p_line->i_width;
771 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
773 i_align_offset = ( i_width - p_line->i_width ) / 2;
777 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
779 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
781 i_offset = ( p_line->p_glyph_pos[ i ].y +
782 i_glyph_tmax - p_glyph->top + 2 ) *
783 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
786 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
788 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
790 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
792 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
799 /* Outlining (find something better than nearest neighbour filtering ?) */
802 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
803 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
804 uint8_t left, current;
806 for( y = 1; y < (int)fmt.i_height - 1; y++ )
808 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
809 p_dst += p_region->p_picture->Y_PITCH;
812 for( x = 1; x < (int)fmt.i_width - 1; x++ )
815 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
816 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;
820 memset( p_top, 0, fmt.i_width );
826 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
827 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
828 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
829 int i_glyph_tmax, int i_align_offset,
830 uint8_t i_y, uint8_t i_u, uint8_t i_v,
831 subpicture_region_t *p_region)
835 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
837 p_dst_y = p_region->p_picture->Y_PIXELS;
838 p_dst_u = p_region->p_picture->U_PIXELS;
839 p_dst_v = p_region->p_picture->V_PIXELS;
840 p_dst_a = p_region->p_picture->A_PIXELS;
841 i_pitch = p_region->p_picture->A_PITCH;
843 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
844 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
846 for( y = 0; y < i_line_thickness; y++ )
848 int i_extra = p_this_glyph->bitmap.width;
852 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
853 (p_this_glyph_pos->x + p_this_glyph->left);
855 for( x = 0; x < i_extra; x++ )
859 /* break the underline around the tails of any glyphs which cross it */
860 for( z = x - i_line_thickness;
861 z < x + i_line_thickness && b_ok;
864 if( p_next_glyph && ( z >= i_extra ) )
866 int i_row = i_line_offset + p_next_glyph->top + y;
868 if( ( p_next_glyph->bitmap.rows > i_row ) &&
869 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
874 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
876 int i_row = i_line_offset + p_this_glyph->top + y;
878 if( ( p_this_glyph->bitmap.rows > i_row ) &&
879 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
888 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
889 p_dst_u[i_offset+x] = i_u;
890 p_dst_v[i_offset+x] = i_v;
891 p_dst_a[i_offset+x] = 255;
898 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
900 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
901 int i_pitch = p_region->p_picture->A_PITCH;
904 for( ; p_line != NULL; p_line = p_line->p_next )
906 int i_glyph_tmax=0, i = 0;
907 int i_bitmap_offset, i_offset, i_align_offset = 0;
908 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
910 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
911 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
914 if( p_line->i_width < i_width )
916 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
918 i_align_offset = i_width - p_line->i_width;
920 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
922 i_align_offset = ( i_width - p_line->i_width ) / 2;
926 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
928 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
930 i_offset = ( p_line->p_glyph_pos[ i ].y +
931 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
932 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
933 i_align_offset +xoffset;
935 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
937 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
939 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
940 if( p_dst[i_offset+x] <
941 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
943 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
952 /*****************************************************************************
953 * Render: place string in picture
954 *****************************************************************************
955 * This function merges the previously rendered freetype glyphs into a picture
956 *****************************************************************************/
957 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
958 line_desc_t *p_line, int i_width, int i_height )
960 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
962 int i, x, y, i_pitch, i_alpha;
963 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
965 if( i_width == 0 || i_height == 0 )
968 /* Create a new subpicture region */
969 memset( &fmt, 0, sizeof(video_format_t) );
970 fmt.i_chroma = VLC_CODEC_YUVA;
972 fmt.i_width = fmt.i_visible_width = i_width + 6;
973 fmt.i_height = fmt.i_visible_height = i_height + 6;
974 if( p_region->fmt.i_visible_width > 0 )
975 fmt.i_visible_width = p_region->fmt.i_visible_width;
976 if( p_region->fmt.i_visible_height > 0 )
977 fmt.i_visible_height = p_region->fmt.i_visible_height;
978 fmt.i_x_offset = fmt.i_y_offset = 0;
980 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
981 if( !p_region->p_picture )
985 /* Calculate text color components */
986 YUVFromRGB( (p_line->i_red << 16) |
987 (p_line->i_green << 8) |
990 i_alpha = p_line->i_alpha;
992 p_dst_y = p_region->p_picture->Y_PIXELS;
993 p_dst_u = p_region->p_picture->U_PIXELS;
994 p_dst_v = p_region->p_picture->V_PIXELS;
995 p_dst_a = p_region->p_picture->A_PIXELS;
996 i_pitch = p_region->p_picture->A_PITCH;
998 /* Initialize the region pixels */
999 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
1001 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
1002 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1003 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1004 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1008 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1009 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1010 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1011 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1013 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1014 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1016 DrawBlack( p_line, i_width, p_region, 0, 0);
1017 DrawBlack( p_line, i_width, p_region, -1, 0);
1018 DrawBlack( p_line, i_width, p_region, 0, -1);
1019 DrawBlack( p_line, i_width, p_region, 1, 0);
1020 DrawBlack( p_line, i_width, p_region, 0, 1);
1023 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1025 DrawBlack( p_line, i_width, p_region, -1, -1);
1026 DrawBlack( p_line, i_width, p_region, -1, 1);
1027 DrawBlack( p_line, i_width, p_region, 1, -1);
1028 DrawBlack( p_line, i_width, p_region, 1, 1);
1030 DrawBlack( p_line, i_width, p_region, -2, 0);
1031 DrawBlack( p_line, i_width, p_region, 0, -2);
1032 DrawBlack( p_line, i_width, p_region, 2, 0);
1033 DrawBlack( p_line, i_width, p_region, 0, 2);
1035 DrawBlack( p_line, i_width, p_region, -2, -2);
1036 DrawBlack( p_line, i_width, p_region, -2, 2);
1037 DrawBlack( p_line, i_width, p_region, 2, -2);
1038 DrawBlack( p_line, i_width, p_region, 2, 2);
1040 DrawBlack( p_line, i_width, p_region, -3, 0);
1041 DrawBlack( p_line, i_width, p_region, 0, -3);
1042 DrawBlack( p_line, i_width, p_region, 3, 0);
1043 DrawBlack( p_line, i_width, p_region, 0, 3);
1046 for( ; p_line != NULL; p_line = p_line->p_next )
1048 int i_glyph_tmax = 0;
1049 int i_bitmap_offset, i_offset, i_align_offset = 0;
1050 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1052 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1053 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1056 if( p_line->i_width < i_width )
1058 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1060 i_align_offset = i_width - p_line->i_width;
1062 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1064 i_align_offset = ( i_width - p_line->i_width ) / 2;
1068 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1070 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1072 i_offset = ( p_line->p_glyph_pos[ i ].y +
1073 i_glyph_tmax - p_glyph->top + 3 ) *
1074 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1077 if( p_line->b_new_color_mode )
1079 /* Every glyph can (and in fact must) have its own color */
1080 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1083 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1085 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1087 uint8_t i_y_local = i_y;
1088 uint8_t i_u_local = i_u;
1089 uint8_t i_v_local = i_v;
1091 if( p_line->p_fg_bg_ratio != 0x00 )
1093 int i_split = p_glyph->bitmap.width *
1094 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1098 YUVFromRGB( p_line->p_bg_rgb[ i ],
1099 &i_y_local, &i_u_local, &i_v_local );
1103 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1105 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1106 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1108 p_dst_u[i_offset+x] = i_u;
1109 p_dst_v[i_offset+x] = i_v;
1111 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1112 p_dst_a[i_offset+x] = 0xff;
1115 i_offset += i_pitch;
1118 if( p_line->pi_underline_thickness[ i ] )
1120 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1121 p_line->pi_underline_offset[ i ],
1122 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1123 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1124 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1125 i_glyph_tmax, i_align_offset,
1132 /* Apply the alpha setting */
1133 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1134 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1140 * This function renders a text subpicture region into another one.
1141 * It also calculates the size needed for this string, and renders the
1142 * needed glyphs into memory. It is used as pf_add_string callback in
1143 * the vout method by this module
1145 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1146 subpicture_region_t *p_region_in )
1148 filter_sys_t *p_sys = p_filter->p_sys;
1149 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1150 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1151 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1152 int i_string_length;
1154 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1155 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1165 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1166 psz_string = p_region_in->psz_text;
1167 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1169 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1170 i_scale = val.i_int;
1172 if( p_region_in->p_style )
1174 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1175 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1176 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1180 i_font_color = p_sys->i_font_color;
1181 i_font_alpha = 255 - p_sys->i_font_opacity;
1182 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1185 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1186 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1187 SetFontSize( p_filter, i_font_size );
1189 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1190 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1191 i_blue = i_font_color & 0x000000FF;
1193 result.x = result.y = 0;
1194 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1196 psz_unicode = psz_unicode_orig =
1197 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1198 if( psz_unicode == NULL )
1200 #if defined(WORDS_BIGENDIAN)
1201 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1203 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1205 if( iconv_handle == (vlc_iconv_t)-1 )
1207 msg_Warn( p_filter, "unable to do conversion" );
1213 const char *p_in_buffer = psz_string;
1214 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1215 i_in_bytes = strlen( psz_string );
1216 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1217 i_out_bytes_left = i_out_bytes;
1218 p_out_buffer = (char *)psz_unicode;
1219 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1221 &p_out_buffer, &i_out_bytes_left );
1223 vlc_iconv_close( iconv_handle );
1227 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1228 "bytes left %u", (unsigned)i_in_bytes );
1231 *(uint32_t*)p_out_buffer = 0;
1232 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1235 #if defined(HAVE_FRIBIDI)
1237 uint32_t *p_fribidi_string;
1238 int32_t start_pos, pos = 0;
1240 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1241 if( !p_fribidi_string )
1244 /* Do bidi conversion line-by-line */
1245 while( pos < i_string_length )
1247 while( pos < i_string_length )
1249 i_char = psz_unicode[pos];
1250 if (i_char != '\r' && i_char != '\n')
1252 p_fribidi_string[pos] = i_char;
1256 while( pos < i_string_length )
1258 i_char = psz_unicode[pos];
1259 if (i_char == '\r' || i_char == '\n')
1263 if (pos > start_pos)
1265 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1266 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1269 (FriBidiChar*)p_fribidi_string + start_pos,
1274 free( psz_unicode_orig );
1275 psz_unicode = psz_unicode_orig = p_fribidi_string;
1276 p_fribidi_string[ i_string_length ] = 0;
1280 /* Calculate relative glyph positions and a bounding box for the
1282 if( !(p_line = NewLine( strlen( psz_string ))) )
1285 i_pen_x = i_pen_y = 0;
1287 psz_line_start = psz_unicode;
1289 #define face p_sys->p_face
1290 #define glyph face->glyph
1292 while( *psz_unicode )
1294 i_char = *psz_unicode++;
1295 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1300 if( i_char == '\n' )
1302 psz_line_start = psz_unicode;
1303 if( !(p_next = NewLine( strlen( psz_string ))) )
1305 p_line->p_next = p_next;
1306 p_line->i_width = line.xMax;
1307 p_line->i_height = face->size->metrics.height >> 6;
1308 p_line->pp_glyphs[ i ] = NULL;
1309 p_line->i_alpha = i_font_alpha;
1310 p_line->i_red = i_red;
1311 p_line->i_green = i_green;
1312 p_line->i_blue = i_blue;
1315 result.x = __MAX( result.x, line.xMax );
1316 result.y += face->size->metrics.height >> 6;
1319 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1320 i_pen_y += face->size->metrics.height >> 6;
1322 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1327 i_glyph_index = FT_Get_Char_Index( face, i_char );
1328 if( p_sys->i_use_kerning && i_glyph_index
1332 FT_Get_Kerning( face, i_previous, i_glyph_index,
1333 ft_kerning_default, &delta );
1334 i_pen_x += delta.x >> 6;
1337 p_line->p_glyph_pos[ i ].x = i_pen_x;
1338 p_line->p_glyph_pos[ i ].y = i_pen_y;
1339 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1342 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1346 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1349 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1353 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1354 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1357 FT_Done_Glyph( tmp_glyph );
1360 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1363 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1364 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1365 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1367 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1368 p_line->pp_glyphs[ i ] = NULL;
1370 p_line = NewLine( strlen( psz_string ));
1371 if( p_prev ) p_prev->p_next = p_line;
1372 else p_lines = p_line;
1374 uint32_t *psz_unicode_saved = psz_unicode;
1375 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1379 if( psz_unicode == psz_line_start )
1380 { /* try harder to break that line */
1381 psz_unicode = psz_unicode_saved;
1382 while( psz_unicode > psz_line_start &&
1383 *psz_unicode != '_' && *psz_unicode != '/' &&
1384 *psz_unicode != '\\' && *psz_unicode != '.' )
1389 if( psz_unicode == psz_line_start )
1391 msg_Warn( p_filter, "unbreakable string" );
1396 *psz_unicode = '\n';
1398 psz_unicode = psz_line_start;
1401 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1404 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1405 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1407 i_previous = i_glyph_index;
1408 i_pen_x += glyph->advance.x >> 6;
1412 p_line->i_width = line.xMax;
1413 p_line->i_height = face->size->metrics.height >> 6;
1414 p_line->pp_glyphs[ i ] = NULL;
1415 p_line->i_alpha = i_font_alpha;
1416 p_line->i_red = i_red;
1417 p_line->i_green = i_green;
1418 p_line->i_blue = i_blue;
1419 result.x = __MAX( result.x, line.xMax );
1420 result.y += line.yMax - line.yMin;
1425 p_region_out->i_x = p_region_in->i_x;
1426 p_region_out->i_y = p_region_in->i_y;
1428 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1429 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1431 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1433 free( psz_unicode_orig );
1434 FreeLines( p_lines );
1438 free( psz_unicode_orig );
1439 FreeLines( p_lines );
1440 return VLC_EGENERIC;
1443 #ifdef HAVE_FONTCONFIG
1444 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1445 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1446 bool b_italic, bool b_uline )
1448 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1452 p_style->i_font_size = i_font_size;
1453 p_style->i_font_color = i_font_color;
1454 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1455 p_style->b_italic = b_italic;
1456 p_style->b_bold = b_bold;
1457 p_style->b_underline = b_uline;
1459 p_style->psz_fontname = strdup( psz_fontname );
1464 static void DeleteStyle( ft_style_t *p_style )
1468 free( p_style->psz_fontname );
1473 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1480 if(( s1->i_font_size == s2->i_font_size ) &&
1481 ( s1->i_font_color == s2->i_font_color ) &&
1482 ( s1->b_italic == s2->b_italic ) &&
1483 ( s1->b_bold == s2->b_bold ) &&
1484 ( s1->b_underline == s2->b_underline ) &&
1485 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1492 static void IconvText( filter_t *p_filter, const char *psz_string,
1493 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1495 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1497 /* If memory hasn't been allocated for our output string, allocate it here
1498 * - the calling function must now be responsible for freeing it.
1500 if( !*ppsz_unicode )
1501 *ppsz_unicode = (uint32_t *)
1502 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1504 /* We don't need to handle a NULL pointer in *ppsz_unicode
1505 * if we are instead testing for a non NULL value like we are here */
1509 #if defined(WORDS_BIGENDIAN)
1510 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1512 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1514 if( iconv_handle != (vlc_iconv_t)-1 )
1516 char *p_in_buffer, *p_out_buffer;
1517 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1518 i_in_bytes = strlen( psz_string );
1519 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1520 i_out_bytes_left = i_out_bytes;
1521 p_in_buffer = (char *) psz_string;
1522 p_out_buffer = (char *) *ppsz_unicode;
1523 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1524 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1526 vlc_iconv_close( iconv_handle );
1530 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1531 "bytes left %u", (unsigned)i_in_bytes );
1535 *(uint32_t*)p_out_buffer = 0;
1537 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1542 msg_Warn( p_filter, "unable to do conversion" );
1547 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1548 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1551 ft_style_t *p_style = NULL;
1553 char *psz_fontname = NULL;
1554 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1555 uint32_t i_karaoke_bg_color = i_font_color;
1556 int i_font_size = p_sys->i_font_size;
1558 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1559 &i_font_color, &i_karaoke_bg_color ))
1561 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1562 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1567 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1568 bool b_uline, int i_karaoke_bgcolor,
1569 line_desc_t *p_line, uint32_t *psz_unicode,
1570 int *pi_pen_x, int i_pen_y, int *pi_start,
1571 FT_Vector *p_result )
1576 bool b_first_on_line = true;
1579 int i_pen_x_start = *pi_pen_x;
1581 uint32_t *psz_unicode_start = psz_unicode;
1583 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1585 /* Account for part of line already in position */
1586 for( i=0; i<*pi_start; i++ )
1590 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1591 ft_glyph_bbox_pixels, &glyph_size );
1593 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1594 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1595 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1596 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1602 b_first_on_line = false;
1604 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1610 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1611 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1615 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1616 ft_kerning_default, &delta );
1617 *pi_pen_x += delta.x >> 6;
1619 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1620 p_line->p_glyph_pos[ i ].y = i_pen_y;
1622 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1626 "unable to render text FT_Load_Glyph returned %d", i_error );
1627 p_line->pp_glyphs[ i ] = NULL;
1628 return VLC_EGENERIC;
1630 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1634 "unable to render text FT_Get_Glyph returned %d", i_error );
1635 p_line->pp_glyphs[ i ] = NULL;
1636 return VLC_EGENERIC;
1638 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1639 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1642 FT_Done_Glyph( tmp_glyph );
1647 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1648 p_face->size->metrics.y_scale));
1649 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1650 p_face->size->metrics.y_scale));
1652 p_line->pi_underline_offset[ i ] =
1653 ( aOffset < 0 ) ? -aOffset : aOffset;
1654 p_line->pi_underline_thickness[ i ] =
1655 ( aSize < 0 ) ? -aSize : aSize;
1657 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1658 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1659 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1660 p_line->p_fg_bg_ratio[ i ] = 0x00;
1662 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1663 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1664 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1666 for( ; i >= *pi_start; i-- )
1667 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1670 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1674 if( psz_unicode == psz_unicode_start )
1676 if( b_first_on_line )
1678 msg_Warn( p_filter, "unbreakable string" );
1679 p_line->pp_glyphs[ i ] = NULL;
1680 return VLC_EGENERIC;
1682 *pi_pen_x = i_pen_x_start;
1684 p_line->i_width = line.xMax;
1685 p_line->i_height = __MAX( p_line->i_height,
1686 p_face->size->metrics.height >> 6 );
1687 p_line->pp_glyphs[ i ] = NULL;
1689 p_result->x = __MAX( p_result->x, line.xMax );
1690 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1691 i_yMax - i_yMin ) );
1696 *psz_unicode = '\n';
1698 psz_unicode = psz_unicode_start;
1699 *pi_pen_x = i_pen_x_start;
1707 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1708 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1710 i_previous = i_glyph_index;
1711 *pi_pen_x += p_face->glyph->advance.x >> 6;
1714 p_line->i_width = line.xMax;
1715 p_line->i_height = __MAX( p_line->i_height,
1716 p_face->size->metrics.height >> 6 );
1717 p_line->pp_glyphs[ i ] = NULL;
1719 p_result->x = __MAX( p_result->x, line.xMax );
1720 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1721 line.yMax - line.yMin ) );
1725 /* Get rid of any text processed - if necessary repositioning
1726 * at the start of a new line of text
1730 *psz_unicode_start = '\0';
1732 else if( psz_unicode > psz_unicode_start )
1734 for( i=0; psz_unicode[ i ]; i++ )
1735 psz_unicode_start[ i ] = psz_unicode[ i ];
1736 psz_unicode_start[ i ] = '\0';
1742 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1743 uint32_t **psz_text_out, uint32_t *pi_runs,
1744 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1745 ft_style_t *p_style )
1747 uint32_t i_string_length = 0;
1749 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1750 *psz_text_out += i_string_length;
1752 if( ppp_styles && ppi_run_lengths )
1758 *ppp_styles = (ft_style_t **)
1759 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1761 else if( *pi_runs == 1 )
1763 *ppp_styles = (ft_style_t **)
1764 malloc( *pi_runs * sizeof( ft_style_t * ) );
1767 /* We have just malloc'ed this memory successfully -
1768 * *pi_runs HAS to be within the memory area of *ppp_styles */
1771 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1775 if( *ppi_run_lengths )
1777 *ppi_run_lengths = (uint32_t *)
1778 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1780 else if( *pi_runs == 1 )
1782 *ppi_run_lengths = (uint32_t *)
1783 malloc( *pi_runs * sizeof( uint32_t ) );
1786 /* same remarks here */
1787 if( *ppi_run_lengths )
1789 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1792 /* If we couldn't use the p_style argument due to memory allocation
1793 * problems above, release it here.
1795 if( p_style ) DeleteStyle( p_style );
1798 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1802 for( k=0; k < p_sys->i_font_attachments; k++ )
1804 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1806 FT_Face p_face = NULL;
1808 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1816 bool match = !strcasecmp( p_face->family_name,
1817 p_style->psz_fontname );
1819 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1820 match = match && p_style->b_bold;
1822 match = match && !p_style->b_bold;
1824 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1825 match = match && p_style->b_italic;
1827 match = match && !p_style->b_italic;
1835 FT_Done_Face( p_face );
1840 return VLC_EGENERIC;
1843 static int ProcessLines( filter_t *p_filter,
1848 uint32_t *pi_run_lengths,
1849 ft_style_t **pp_styles,
1850 line_desc_t **pp_lines,
1852 FT_Vector *p_result,
1856 uint32_t *pi_k_run_lengths,
1857 uint32_t *pi_k_durations )
1859 filter_sys_t *p_sys = p_filter->p_sys;
1860 ft_style_t **pp_char_styles;
1861 int *p_new_positions = NULL;
1862 int8_t *p_levels = NULL;
1863 uint8_t *pi_karaoke_bar = NULL;
1867 /* Assign each character in the text string its style explicitly, so that
1868 * after the characters have been shuffled around by Fribidi, we can re-apply
1869 * the styles, and to simplify the calculation of runs within a line.
1871 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1872 if( !pp_char_styles )
1877 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1878 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1879 * we just won't be able to display the progress bar; at least we'll
1885 for( j = 0; j < i_runs; j++ )
1886 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1887 pp_char_styles[ i++ ] = pp_styles[ j ];
1889 #if defined(HAVE_FRIBIDI)
1891 ft_style_t **pp_char_styles_new;
1892 int *p_old_positions;
1893 uint32_t *p_fribidi_string;
1894 int start_pos, pos = 0;
1896 pp_char_styles_new = (ft_style_t **)
1897 malloc( i_len * sizeof( ft_style_t * ));
1899 p_fribidi_string = (uint32_t *)
1900 malloc( (i_len + 1) * sizeof(uint32_t) );
1901 p_old_positions = (int *)
1902 malloc( (i_len + 1) * sizeof( int ) );
1903 p_new_positions = (int *)
1904 malloc( (i_len + 1) * sizeof( int ) );
1905 p_levels = (int8_t *)
1906 malloc( (i_len + 1) * sizeof( int8_t ) );
1908 if( ! pp_char_styles_new ||
1909 ! p_fribidi_string ||
1910 ! p_old_positions ||
1911 ! p_new_positions ||
1915 free( p_old_positions );
1916 free( p_new_positions );
1917 free( p_fribidi_string );
1918 free( pp_char_styles_new );
1919 free( pi_karaoke_bar );
1921 free( pp_char_styles );
1925 /* Do bidi conversion line-by-line */
1928 while(pos < i_len) {
1929 if (psz_text[pos] != '\n')
1931 p_fribidi_string[pos] = psz_text[pos];
1932 pp_char_styles_new[pos] = pp_char_styles[pos];
1933 p_new_positions[pos] = pos;
1938 while(pos < i_len) {
1939 if (psz_text[pos] == '\n')
1943 if (pos > start_pos)
1945 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1946 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1947 pos - start_pos, &base_dir,
1948 (FriBidiChar*)p_fribidi_string + start_pos,
1949 p_new_positions + start_pos,
1951 p_levels + start_pos );
1952 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1954 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1955 p_old_positions[ j - start_pos ] ];
1956 p_new_positions[ j ] += start_pos;
1960 free( p_old_positions );
1961 free( pp_char_styles );
1962 pp_char_styles = pp_char_styles_new;
1963 psz_text = p_fribidi_string;
1964 p_fribidi_string[ i_len ] = 0;
1967 /* Work out the karaoke */
1968 if( pi_karaoke_bar )
1970 int64_t i_last_duration = 0;
1971 int64_t i_duration = 0;
1972 int64_t i_start_pos = 0;
1973 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1975 for( k = 0; k< i_k_runs; k++ )
1977 double fraction = 0.0;
1979 i_duration += pi_k_durations[ k ];
1981 if( i_duration < i_elapsed )
1983 /* Completely finished this run-length -
1984 * let it render normally */
1988 else if( i_elapsed < i_last_duration )
1990 /* Haven't got up to this segment yet -
1991 * render it completely in karaoke BG mode */
1997 /* Partway through this run */
1999 fraction = (double)(i_elapsed - i_last_duration) /
2000 (double)pi_k_durations[ k ];
2002 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2004 double shade = pi_k_run_lengths[ k ] * fraction;
2006 if( p_new_positions )
2007 j = p_new_positions[ i_start_pos + i ];
2009 j = i_start_pos + i;
2011 if( i < (uint32_t)shade )
2012 pi_karaoke_bar[ j ] = 0xff;
2013 else if( (double)i > shade )
2014 pi_karaoke_bar[ j ] = 0x00;
2017 shade -= (int)shade;
2018 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2019 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2023 i_last_duration = i_duration;
2024 i_start_pos += pi_k_run_lengths[ k ];
2028 free( p_new_positions );
2030 FT_Vector tmp_result;
2032 line_desc_t *p_line = NULL;
2033 line_desc_t *p_prev = NULL;
2039 p_result->x = p_result->y = 0;
2040 tmp_result.x = tmp_result.y = 0;
2043 for( k = 0; k <= (uint32_t) i_len; k++ )
2045 if( ( k == (uint32_t) i_len ) ||
2047 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2049 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2051 /* End of the current style run */
2052 FT_Face p_face = NULL;
2055 /* Look for a match amongst our attachments first */
2056 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2060 char *psz_fontfile = NULL;
2062 vlc_mutex_lock( &fb_lock );
2063 if( p_sys->b_fontconfig_ok )
2065 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2066 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2067 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2068 p_style->psz_fontname,
2073 vlc_mutex_unlock( &fb_lock );
2075 if( psz_fontfile && ! *psz_fontfile )
2077 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2078 " so using default font", p_style->psz_fontname,
2079 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2080 (p_style->b_bold ? "(Bold)" :
2081 (p_style->b_italic ? "(Italic)" : ""))) );
2082 free( psz_fontfile );
2083 psz_fontfile = NULL;
2088 if( FT_New_Face( p_sys->p_library,
2089 psz_fontfile, i_idx, &p_face ) )
2091 free( psz_fontfile );
2092 free( pp_char_styles );
2093 #if defined(HAVE_FRIBIDI)
2096 free( pi_karaoke_bar );
2097 return VLC_EGENERIC;
2099 free( psz_fontfile );
2103 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2105 /* We've loaded a font face which is unhelpful for actually
2106 * rendering text - fallback to the default one.
2108 FT_Done_Face( p_face );
2112 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2113 ft_encoding_unicode ) ||
2114 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2115 p_style->i_font_size ) )
2117 if( p_face ) FT_Done_Face( p_face );
2118 free( pp_char_styles );
2119 #if defined(HAVE_FRIBIDI)
2122 free( pi_karaoke_bar );
2123 return VLC_EGENERIC;
2125 p_sys->i_use_kerning =
2126 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2129 uint32_t *psz_unicode = (uint32_t *)
2130 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2133 if( p_face ) FT_Done_Face( p_face );
2134 free( pp_char_styles );
2135 free( psz_unicode );
2136 #if defined(HAVE_FRIBIDI)
2139 free( pi_karaoke_bar );
2142 memcpy( psz_unicode, psz_text + i_prev,
2143 sizeof( uint32_t ) * ( k - i_prev ) );
2144 psz_unicode[ k - i_prev ] = 0;
2145 while( *psz_unicode )
2149 if( !(p_line = NewLine( i_len - i_prev)) )
2151 if( p_face ) FT_Done_Face( p_face );
2152 free( pp_char_styles );
2153 free( psz_unicode );
2154 #if defined(HAVE_FRIBIDI)
2157 free( pi_karaoke_bar );
2160 /* New Color mode only works in YUVA rendering mode --
2161 * (RGB mode has palette constraints on it). We therefore
2162 * need to populate the legacy colour fields also.
2164 p_line->b_new_color_mode = true;
2165 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2166 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2167 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2168 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2169 p_line->p_next = NULL;
2171 i_pen_y += tmp_result.y;
2175 if( p_prev ) p_prev->p_next = p_line;
2176 else *pp_lines = p_line;
2179 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2180 p_style->i_font_color, p_style->b_underline,
2181 p_style->i_karaoke_bg_color,
2182 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2183 &tmp_result ) != VLC_SUCCESS )
2185 if( p_face ) FT_Done_Face( p_face );
2186 free( pp_char_styles );
2187 free( psz_unicode );
2188 #if defined(HAVE_FRIBIDI)
2191 free( pi_karaoke_bar );
2192 return VLC_EGENERIC;
2197 p_result->x = __MAX( p_result->x, tmp_result.x );
2198 p_result->y += tmp_result.y;
2203 if( *psz_unicode == '\n')
2207 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2209 *c_ptr = *(c_ptr+1);
2214 free( psz_unicode );
2215 if( p_face ) FT_Done_Face( p_face );
2219 free( pp_char_styles );
2220 #if defined(HAVE_FRIBIDI)
2225 p_result->x = __MAX( p_result->x, tmp_result.x );
2226 p_result->y += tmp_result.y;
2229 if( pi_karaoke_bar )
2232 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2234 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2236 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2240 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2242 /* 100% BG colour will render faster if we
2243 * instead make it 100% FG colour, so leave
2244 * the ratio alone and copy the value across
2246 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2250 if( pi_karaoke_bar[ i ] & 0x80 )
2252 /* Swap Left and Right sides over for Right aligned
2253 * language text (eg. Arabic, Hebrew)
2255 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2257 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2258 p_line->p_bg_rgb[ k ] = i_tmp;
2260 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2263 /* Jump over the '\n' at the line-end */
2266 free( pi_karaoke_bar );
2272 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2273 subpicture_region_t *p_region_in )
2275 int rv = VLC_SUCCESS;
2276 stream_t *p_sub = NULL;
2277 xml_t *p_xml = NULL;
2278 xml_reader_t *p_xml_reader = NULL;
2280 if( !p_region_in || !p_region_in->psz_html )
2281 return VLC_EGENERIC;
2283 /* Reset the default fontsize in case screen metrics have changed */
2284 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2286 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2287 (uint8_t *) p_region_in->psz_html,
2288 strlen( p_region_in->psz_html ),
2292 p_xml = xml_Create( p_filter );
2295 bool b_karaoke = false;
2297 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2300 /* Look for Root Node */
2301 if( xml_ReaderRead( p_xml_reader ) == 1 )
2303 char *psz_node = xml_ReaderName( p_xml_reader );
2305 if( !strcasecmp( "karaoke", psz_node ) )
2307 /* We're going to have to render the text a number
2308 * of times to show the progress marker on the text.
2310 var_SetBool( p_filter, "text-rerender", true );
2313 else if( !strcasecmp( "text", psz_node ) )
2319 /* Only text and karaoke tags are supported */
2320 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2321 xml_ReaderDelete( p_xml, p_xml_reader );
2322 p_xml_reader = NULL;
2334 uint32_t i_runs = 0;
2335 uint32_t i_k_runs = 0;
2336 uint32_t *pi_run_lengths = NULL;
2337 uint32_t *pi_k_run_lengths = NULL;
2338 uint32_t *pi_k_durations = NULL;
2339 ft_style_t **pp_styles = NULL;
2341 line_desc_t *p_lines = NULL;
2343 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2344 sizeof( uint32_t ) );
2349 rv = ProcessNodes( p_filter, p_xml_reader,
2350 p_region_in->p_style, psz_text, &i_len,
2351 &i_runs, &pi_run_lengths, &pp_styles,
2353 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2356 p_region_out->i_x = p_region_in->i_x;
2357 p_region_out->i_y = p_region_in->i_y;
2359 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2361 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2362 pi_run_lengths, pp_styles, &p_lines, &result,
2363 b_karaoke, i_k_runs, pi_k_run_lengths,
2367 for( k=0; k<i_runs; k++)
2368 DeleteStyle( pp_styles[k] );
2370 free( pi_run_lengths );
2373 /* Don't attempt to render text that couldn't be layed out
2376 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2378 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2380 Render( p_filter, p_region_out, p_lines,
2381 result.x, result.y );
2385 RenderYUVA( p_filter, p_region_out, p_lines,
2386 result.x, result.y );
2390 FreeLines( p_lines );
2392 xml_ReaderDelete( p_xml, p_xml_reader );
2394 xml_Delete( p_xml );
2396 stream_Delete( p_sub );
2402 static char* FontConfig_Select( FcConfig* priv, const char* family,
2403 bool b_bold, bool b_italic, int *i_idx )
2406 FcPattern *pat, *p_pat;
2410 pat = FcPatternCreate();
2411 if (!pat) return NULL;
2413 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2414 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2415 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2416 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2418 FcDefaultSubstitute( pat );
2420 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2422 FcPatternDestroy( pat );
2426 p_pat = FcFontMatch( priv, pat, &result );
2427 FcPatternDestroy( pat );
2428 if( !p_pat ) return NULL;
2430 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2431 || ( val_b != FcTrue ) )
2433 FcPatternDestroy( p_pat );
2436 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2441 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2443 FcPatternDestroy( p_pat );
2448 if( strcasecmp((const char*)val_s, family ) != 0 )
2449 msg_Warn( p_filter, "fontconfig: selected font family is not"
2450 "the requested one: '%s' != '%s'\n",
2451 (const char*)val_s, family );
2454 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2456 FcPatternDestroy( p_pat );
2460 FcPatternDestroy( p_pat );
2461 return strdup( (const char*)val_s );
2465 static void FreeLine( line_desc_t *p_line )
2468 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2470 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2472 free( p_line->pp_glyphs );
2473 free( p_line->p_glyph_pos );
2474 free( p_line->p_fg_rgb );
2475 free( p_line->p_bg_rgb );
2476 free( p_line->p_fg_bg_ratio );
2477 free( p_line->pi_underline_offset );
2478 free( p_line->pi_underline_thickness );
2482 static void FreeLines( line_desc_t *p_lines )
2484 line_desc_t *p_line, *p_next;
2486 if( !p_lines ) return;
2488 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2490 p_next = p_line->p_next;
2495 static line_desc_t *NewLine( int i_count )
2497 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2499 if( !p_line ) return NULL;
2500 p_line->i_height = 0;
2501 p_line->i_width = 0;
2502 p_line->p_next = NULL;
2504 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2505 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2506 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2507 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2508 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2509 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2510 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2511 if( ( p_line->pp_glyphs == NULL ) ||
2512 ( p_line->p_glyph_pos == NULL ) ||
2513 ( p_line->p_fg_rgb == NULL ) ||
2514 ( p_line->p_bg_rgb == NULL ) ||
2515 ( p_line->p_fg_bg_ratio == NULL ) ||
2516 ( p_line->pi_underline_offset == NULL ) ||
2517 ( p_line->pi_underline_thickness == NULL ) )
2519 free( p_line->pi_underline_thickness );
2520 free( p_line->pi_underline_offset );
2521 free( p_line->p_fg_rgb );
2522 free( p_line->p_bg_rgb );
2523 free( p_line->p_fg_bg_ratio );
2524 free( p_line->p_glyph_pos );
2525 free( p_line->pp_glyphs );
2529 p_line->pp_glyphs[0] = NULL;
2530 p_line->b_new_color_mode = false;
2535 static int GetFontSize( filter_t *p_filter )
2537 filter_sys_t *p_sys = p_filter->p_sys;
2541 if( p_sys->i_default_font_size )
2543 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2544 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2546 i_size = p_sys->i_default_font_size;
2550 var_Get( p_filter, "freetype-rel-fontsize", &val );
2553 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2554 p_filter->p_sys->i_display_height =
2555 p_filter->fmt_out.video.i_height;
2560 msg_Warn( p_filter, "invalid fontsize, using 12" );
2561 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2562 i_size = 12 * val.i_int / 1000;
2569 static int SetFontSize( filter_t *p_filter, int i_size )
2571 filter_sys_t *p_sys = p_filter->p_sys;
2575 i_size = GetFontSize( p_filter );
2577 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2580 p_sys->i_font_size = i_size;
2582 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2584 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2585 return VLC_EGENERIC;
2591 static void YUVFromRGB( uint32_t i_argb,
2592 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2594 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2595 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2596 int i_blue = ( i_argb & 0x000000ff );
2598 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2599 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2600 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2601 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2602 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2603 -585 * i_blue + 4096 + 1048576) >> 13, 240);