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_font( "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 char* psz_fontfamily;
271 bool b_fontconfig_ok;
272 FcConfig *p_fontconfig;
275 input_attachment_t **pp_font_attachments;
276 int i_font_attachments;
278 vlc_object_t *p_fontbuilder;
281 #define UCHAR uint32_t
282 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
283 #define TR_FONT_STYLE_PTR ft_style_t *
285 #include "text_renderer.h"
287 /*****************************************************************************
288 * Create: allocates osd-text video thread output method
289 *****************************************************************************
290 * This function allocates and initializes a Clone vout method.
291 *****************************************************************************/
292 static int Create( vlc_object_t *p_this )
294 filter_t *p_filter = (filter_t *)p_this;
296 char *psz_fontfile=NULL;
297 char *psz_fontfamily=NULL;
298 int i_error,fontindex;
300 #ifdef HAVE_FONTCONFIG
301 FcPattern *fontpattern = NULL, *fontmatch = NULL;
302 /* Initialise result to Match, as fontconfig doesnt
303 * really set this other than some error-cases */
304 FcResult fontresult = FcResultMatch;
308 /* Allocate structure */
309 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
312 #ifdef HAVE_FONTCONFIG
313 p_sys->psz_fontfamily = NULL;
316 p_sys->p_library = 0;
317 p_sys->i_font_size = 0;
318 p_sys->i_display_height = 0;
320 var_Create( p_filter, "freetype-rel-fontsize",
321 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
323 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
324 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
325 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
326 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
327 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
328 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
329 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
332 if( !psz_fontfamily || !*psz_fontfamily )
334 #ifdef HAVE_FONTCONFIG
335 free( psz_fontfamily);
336 psz_fontfamily=strdup( DEFAULT_FONT );
338 free( psz_fontfamily );
339 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
340 if( !psz_fontfamily )
343 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
344 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
346 strcpy( psz_fontfamily, DEFAULT_FONT );
348 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
352 #ifdef HAVE_FONTCONFIG
353 /* Lets find some fontfile from freetype-font variable family */
355 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
358 fontpattern = FcPatternCreate();
363 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
364 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
365 free( psz_fontsize );
367 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
369 FcDefaultSubstitute( fontpattern );
371 /* testing fontresult here doesn't do any good really, but maybe it will
372 * in future as fontconfig code doesn't set it in all cases and just
373 * returns NULL or doesn't set to to Match on all Match cases.*/
374 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
375 if( !fontmatch || fontresult == FcResultNoMatch )
378 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
379 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
382 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
383 p_sys->psz_fontfamily = strdup( psz_fontfamily );
385 p_sys->psz_fontfamily = strdup( DEFAULT_FONT )
386 psz_fontfile = psz_fontfamily;
389 i_error = FT_Init_FreeType( &p_sys->p_library );
392 msg_Err( p_filter, "couldn't initialize freetype" );
396 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
397 fontindex, &p_sys->p_face );
399 if( i_error == FT_Err_Unknown_File_Format )
401 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
406 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
410 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
413 msg_Err( p_filter, "font has no unicode translation table" );
417 #ifdef HAVE_FONTCONFIG
418 p_sys->b_fontconfig_ok = false;
419 p_sys->p_fontconfig = NULL;
420 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
423 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
425 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
428 p_sys->pp_font_attachments = NULL;
429 p_sys->i_font_attachments = 0;
431 p_filter->pf_render_text = RenderText;
432 #ifdef HAVE_FONTCONFIG
433 p_filter->pf_render_html = RenderHtml;
434 FcPatternDestroy( fontmatch );
435 FcPatternDestroy( fontpattern );
437 p_filter->pf_render_html = NULL;
440 free( psz_fontfamily );
441 LoadFontsFromAttachments( p_filter );
446 #ifdef HAVE_FONTCONFIG
447 if( fontmatch ) FcPatternDestroy( fontmatch );
448 if( fontpattern ) FcPatternDestroy( fontpattern );
450 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
451 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
452 free( psz_fontfamily );
457 /*****************************************************************************
458 * Destroy: destroy Clone video thread output method
459 *****************************************************************************
460 * Clean up all data and library connections
461 *****************************************************************************/
462 static void Destroy( vlc_object_t *p_this )
464 filter_t *p_filter = (filter_t *)p_this;
465 filter_sys_t *p_sys = p_filter->p_sys;
467 if( p_sys->pp_font_attachments )
471 for( k = 0; k < p_sys->i_font_attachments; k++ )
472 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
474 free( p_sys->pp_font_attachments );
477 #ifdef HAVE_FONTCONFIG
478 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
479 free( p_sys->psz_fontfamily );
482 /* FcFini asserts calling the subfunction FcCacheFini()
483 * even if no other library functions have been made since FcInit(),
484 * so don't call it. */
486 FT_Done_Face( p_sys->p_face );
487 FT_Done_FreeType( p_sys->p_library );
491 #ifdef HAVE_FONTCONFIG
492 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
494 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
496 /* Check for an existing Fontbuilder thread */
497 vlc_mutex_lock( &fb_lock );
498 vlc_object_t *p_fontbuilder =
499 vlc_object_find_name( p_filter->p_libvlc,
500 "fontlist builder", FIND_CHILD );
504 /* Create the FontBuilderThread thread as a child of a top-level
505 * object, so that it can survive the destruction of the
506 * freetype object - the fontlist only needs to be built once,
507 * and calling the fontbuild a second time while the first is
508 * still in progress can cause thread instabilities.
510 * XXX The fontbuilder will be destroy as soon as it is unused.
513 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
514 sizeof(vlc_object_t) );
517 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
518 p_fontbuilder->p_private = NULL;
519 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
521 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
523 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
524 var_SetBool( p_fontbuilder, "build-done", false );
525 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
526 var_SetBool( p_fontbuilder, "build-joined", false );
528 if( vlc_thread_create( p_fontbuilder,
531 VLC_THREAD_PRIORITY_LOW ) )
533 msg_Warn( p_filter, "fontconfig database builder thread can't "
534 "be launched. Font styling support will be limited." );
541 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
542 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
544 vlc_mutex_unlock( &fb_lock );
545 return p_fontbuilder;
547 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
549 vlc_mutex_lock( &fb_lock );
552 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
554 /* We wait for the thread on the first FontBuilderDetach */
555 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
557 var_SetBool( p_fontbuilder, "build-joined", true );
558 vlc_mutex_unlock( &fb_lock );
559 /* We need to unlock otherwise we may not join (the thread waiting
560 * for the lock). It is safe to unlock as no one else will try a
561 * join and we have a reference on the object) */
562 vlc_thread_join( p_fontbuilder );
563 vlc_mutex_lock( &fb_lock );
565 vlc_object_release( p_fontbuilder );
567 vlc_mutex_unlock( &fb_lock );
569 static void* FontBuilderThread( vlc_object_t *p_this )
571 FcConfig *p_fontconfig = FcInitLoadConfig();
576 int canc = vlc_savecancel ();
578 //msg_Dbg( p_this, "Building font database..." );
579 msg_Dbg( p_this, "Building font database..." );
581 if(! FcConfigBuildFonts( p_fontconfig ))
583 /* Don't destroy the fontconfig object - we won't be able to do
584 * italics or bold or change the font face, but we will still
585 * be able to do underline and change the font size.
587 msg_Err( p_this, "fontconfig database can't be built. "
588 "Font styling won't be available" );
592 msg_Dbg( p_this, "Finished building font database." );
593 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
595 vlc_mutex_lock( &fb_lock );
596 p_this->p_private = p_fontconfig;
597 vlc_mutex_unlock( &fb_lock );
599 var_SetBool( p_this, "build-done", true );
600 vlc_restorecancel (canc);
604 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
606 filter_sys_t *p_sys = p_filter->p_sys;
608 p_sys->p_fontconfig = p_fontbuilder->p_private;
609 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
611 static void FontBuilderDestructor( vlc_object_t *p_this )
613 FcConfig *p_fontconfig = p_this->p_private;
616 FcConfigDestroy( p_fontconfig );
618 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
619 vlc_value_t oldval, vlc_value_t newval, void *param )
621 filter_t *p_filter = param;
625 vlc_mutex_lock( &fb_lock );
627 FontBuilderGetFcConfig( p_filter, p_this );
629 vlc_mutex_unlock( &fb_lock );
638 /*****************************************************************************
639 * Make any TTF/OTF fonts present in the attachments of the media file
640 * and store them for later use by the FreeType Engine
641 *****************************************************************************/
642 static int LoadFontsFromAttachments( filter_t *p_filter )
644 filter_sys_t *p_sys = p_filter->p_sys;
645 input_thread_t *p_input;
646 input_attachment_t **pp_attachments;
647 int i_attachments_cnt;
649 int rv = VLC_SUCCESS;
651 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
655 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
657 vlc_object_release(p_input);
661 p_sys->i_font_attachments = 0;
662 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
663 if(! p_sys->pp_font_attachments )
666 for( k = 0; k < i_attachments_cnt; k++ )
668 input_attachment_t *p_attach = pp_attachments[k];
670 if( p_sys->pp_font_attachments )
672 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
673 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
674 ( p_attach->i_data > 0 ) &&
675 ( p_attach->p_data != NULL ) )
677 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
681 vlc_input_attachment_Delete( p_attach );
686 vlc_input_attachment_Delete( p_attach );
689 free( pp_attachments );
691 vlc_object_release(p_input);
696 /*****************************************************************************
697 * Render: place string in picture
698 *****************************************************************************
699 * This function merges the previously rendered freetype glyphs into a picture
700 *****************************************************************************/
701 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
702 line_desc_t *p_line, int i_width, int i_height )
704 static const uint8_t pi_gamma[16] =
705 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
706 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
710 int i, x, y, i_pitch;
711 uint8_t i_y; /* YUV values, derived from incoming RGB */
714 /* Create a new subpicture region */
715 memset( &fmt, 0, sizeof(video_format_t) );
716 fmt.i_chroma = VLC_CODEC_YUVP;
718 fmt.i_width = fmt.i_visible_width = i_width + 4;
719 fmt.i_height = fmt.i_visible_height = i_height + 4;
720 if( p_region->fmt.i_visible_width > 0 )
721 fmt.i_visible_width = p_region->fmt.i_visible_width;
722 if( p_region->fmt.i_visible_height > 0 )
723 fmt.i_visible_height = p_region->fmt.i_visible_height;
724 fmt.i_x_offset = fmt.i_y_offset = 0;
726 assert( !p_region->p_picture );
727 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
728 if( !p_region->p_picture )
732 /* Calculate text color components */
733 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
734 25 * p_line->i_blue + 128) >> 8) + 16;
735 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
736 112 * p_line->i_blue + 128) >> 8) + 128;
737 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
738 18 * p_line->i_blue + 128) >> 8) + 128;
741 fmt.p_palette->i_entries = 16;
742 for( i = 0; i < 8; i++ )
744 fmt.p_palette->palette[i][0] = 0;
745 fmt.p_palette->palette[i][1] = 0x80;
746 fmt.p_palette->palette[i][2] = 0x80;
747 fmt.p_palette->palette[i][3] = pi_gamma[i];
748 fmt.p_palette->palette[i][3] =
749 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
751 for( i = 8; i < fmt.p_palette->i_entries; i++ )
753 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
754 fmt.p_palette->palette[i][1] = i_u;
755 fmt.p_palette->palette[i][2] = i_v;
756 fmt.p_palette->palette[i][3] = pi_gamma[i];
757 fmt.p_palette->palette[i][3] =
758 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
761 p_dst = p_region->p_picture->Y_PIXELS;
762 i_pitch = p_region->p_picture->Y_PITCH;
764 /* Initialize the region pixels */
765 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
767 for( ; p_line != NULL; p_line = p_line->p_next )
769 int i_glyph_tmax = 0;
770 int i_bitmap_offset, i_offset, i_align_offset = 0;
771 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
773 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
774 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
777 if( p_line->i_width < i_width )
779 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
781 i_align_offset = i_width - p_line->i_width;
783 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
785 i_align_offset = ( i_width - p_line->i_width ) / 2;
789 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
791 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
793 i_offset = ( p_line->p_glyph_pos[ i ].y +
794 i_glyph_tmax - p_glyph->top + 2 ) *
795 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
798 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
800 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
802 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
804 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
811 /* Outlining (find something better than nearest neighbour filtering ?) */
814 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
815 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
816 uint8_t left, current;
818 for( y = 1; y < (int)fmt.i_height - 1; y++ )
820 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
821 p_dst += p_region->p_picture->Y_PITCH;
824 for( x = 1; x < (int)fmt.i_width - 1; x++ )
827 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
828 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;
832 memset( p_top, 0, fmt.i_width );
838 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
839 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
840 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
841 int i_glyph_tmax, int i_align_offset,
842 uint8_t i_y, uint8_t i_u, uint8_t i_v,
843 subpicture_region_t *p_region)
847 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
849 p_dst_y = p_region->p_picture->Y_PIXELS;
850 p_dst_u = p_region->p_picture->U_PIXELS;
851 p_dst_v = p_region->p_picture->V_PIXELS;
852 p_dst_a = p_region->p_picture->A_PIXELS;
853 i_pitch = p_region->p_picture->A_PITCH;
855 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
856 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
858 for( y = 0; y < i_line_thickness; y++ )
860 int i_extra = p_this_glyph->bitmap.width;
864 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
865 (p_this_glyph_pos->x + p_this_glyph->left);
867 for( x = 0; x < i_extra; x++ )
871 /* break the underline around the tails of any glyphs which cross it */
872 for( z = x - i_line_thickness;
873 z < x + i_line_thickness && b_ok;
876 if( p_next_glyph && ( z >= i_extra ) )
878 int i_row = i_line_offset + p_next_glyph->top + y;
880 if( ( p_next_glyph->bitmap.rows > i_row ) &&
881 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
886 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
888 int i_row = i_line_offset + p_this_glyph->top + y;
890 if( ( p_this_glyph->bitmap.rows > i_row ) &&
891 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
900 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
901 p_dst_u[i_offset+x] = i_u;
902 p_dst_v[i_offset+x] = i_v;
903 p_dst_a[i_offset+x] = 255;
910 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
912 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
913 int i_pitch = p_region->p_picture->A_PITCH;
916 for( ; p_line != NULL; p_line = p_line->p_next )
918 int i_glyph_tmax=0, i = 0;
919 int i_bitmap_offset, i_offset, i_align_offset = 0;
920 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
922 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
923 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
926 if( p_line->i_width < i_width )
928 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
930 i_align_offset = i_width - p_line->i_width;
932 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
934 i_align_offset = ( i_width - p_line->i_width ) / 2;
938 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
940 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
942 i_offset = ( p_line->p_glyph_pos[ i ].y +
943 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
944 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
945 i_align_offset +xoffset;
947 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
949 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
951 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
952 if( p_dst[i_offset+x] <
953 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
955 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
964 /*****************************************************************************
965 * Render: place string in picture
966 *****************************************************************************
967 * This function merges the previously rendered freetype glyphs into a picture
968 *****************************************************************************/
969 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
970 line_desc_t *p_line, int i_width, int i_height )
972 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
974 int i, x, y, i_pitch, i_alpha;
975 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
977 if( i_width == 0 || i_height == 0 )
980 /* Create a new subpicture region */
981 memset( &fmt, 0, sizeof(video_format_t) );
982 fmt.i_chroma = VLC_CODEC_YUVA;
984 fmt.i_width = fmt.i_visible_width = i_width + 6;
985 fmt.i_height = fmt.i_visible_height = i_height + 6;
986 if( p_region->fmt.i_visible_width > 0 )
987 fmt.i_visible_width = p_region->fmt.i_visible_width;
988 if( p_region->fmt.i_visible_height > 0 )
989 fmt.i_visible_height = p_region->fmt.i_visible_height;
990 fmt.i_x_offset = fmt.i_y_offset = 0;
992 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
993 if( !p_region->p_picture )
997 /* Calculate text color components */
998 YUVFromRGB( (p_line->i_red << 16) |
999 (p_line->i_green << 8) |
1002 i_alpha = p_line->i_alpha;
1004 p_dst_y = p_region->p_picture->Y_PIXELS;
1005 p_dst_u = p_region->p_picture->U_PIXELS;
1006 p_dst_v = p_region->p_picture->V_PIXELS;
1007 p_dst_a = p_region->p_picture->A_PIXELS;
1008 i_pitch = p_region->p_picture->A_PITCH;
1010 /* Initialize the region pixels */
1011 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
1013 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
1014 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1015 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1016 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1020 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1021 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1022 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1023 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1025 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1026 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1028 DrawBlack( p_line, i_width, p_region, 0, 0);
1029 DrawBlack( p_line, i_width, p_region, -1, 0);
1030 DrawBlack( p_line, i_width, p_region, 0, -1);
1031 DrawBlack( p_line, i_width, p_region, 1, 0);
1032 DrawBlack( p_line, i_width, p_region, 0, 1);
1035 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1037 DrawBlack( p_line, i_width, p_region, -1, -1);
1038 DrawBlack( p_line, i_width, p_region, -1, 1);
1039 DrawBlack( p_line, i_width, p_region, 1, -1);
1040 DrawBlack( p_line, i_width, p_region, 1, 1);
1042 DrawBlack( p_line, i_width, p_region, -2, 0);
1043 DrawBlack( p_line, i_width, p_region, 0, -2);
1044 DrawBlack( p_line, i_width, p_region, 2, 0);
1045 DrawBlack( p_line, i_width, p_region, 0, 2);
1047 DrawBlack( p_line, i_width, p_region, -2, -2);
1048 DrawBlack( p_line, i_width, p_region, -2, 2);
1049 DrawBlack( p_line, i_width, p_region, 2, -2);
1050 DrawBlack( p_line, i_width, p_region, 2, 2);
1052 DrawBlack( p_line, i_width, p_region, -3, 0);
1053 DrawBlack( p_line, i_width, p_region, 0, -3);
1054 DrawBlack( p_line, i_width, p_region, 3, 0);
1055 DrawBlack( p_line, i_width, p_region, 0, 3);
1058 for( ; p_line != NULL; p_line = p_line->p_next )
1060 int i_glyph_tmax = 0;
1061 int i_bitmap_offset, i_offset, i_align_offset = 0;
1062 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1064 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1065 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1068 if( p_line->i_width < i_width )
1070 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1072 i_align_offset = i_width - p_line->i_width;
1074 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1076 i_align_offset = ( i_width - p_line->i_width ) / 2;
1080 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1082 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1084 i_offset = ( p_line->p_glyph_pos[ i ].y +
1085 i_glyph_tmax - p_glyph->top + 3 ) *
1086 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1089 if( p_line->b_new_color_mode )
1091 /* Every glyph can (and in fact must) have its own color */
1092 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1095 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1097 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1099 uint8_t i_y_local = i_y;
1100 uint8_t i_u_local = i_u;
1101 uint8_t i_v_local = i_v;
1103 if( p_line->p_fg_bg_ratio != 0x00 )
1105 int i_split = p_glyph->bitmap.width *
1106 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1110 YUVFromRGB( p_line->p_bg_rgb[ i ],
1111 &i_y_local, &i_u_local, &i_v_local );
1115 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1117 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1118 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1120 p_dst_u[i_offset+x] = i_u;
1121 p_dst_v[i_offset+x] = i_v;
1123 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1124 p_dst_a[i_offset+x] = 0xff;
1127 i_offset += i_pitch;
1130 if( p_line->pi_underline_thickness[ i ] )
1132 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1133 p_line->pi_underline_offset[ i ],
1134 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1135 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1136 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1137 i_glyph_tmax, i_align_offset,
1144 /* Apply the alpha setting */
1145 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1146 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1152 * This function renders a text subpicture region into another one.
1153 * It also calculates the size needed for this string, and renders the
1154 * needed glyphs into memory. It is used as pf_add_string callback in
1155 * the vout method by this module
1157 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1158 subpicture_region_t *p_region_in )
1160 filter_sys_t *p_sys = p_filter->p_sys;
1161 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1162 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1163 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1164 int i_string_length;
1166 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1167 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1177 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1178 psz_string = p_region_in->psz_text;
1179 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1181 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1182 i_scale = val.i_int;
1184 if( p_region_in->p_style )
1186 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1187 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1188 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1192 i_font_color = p_sys->i_font_color;
1193 i_font_alpha = 255 - p_sys->i_font_opacity;
1194 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1197 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1198 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1199 SetFontSize( p_filter, i_font_size );
1201 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1202 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1203 i_blue = i_font_color & 0x000000FF;
1205 result.x = result.y = 0;
1206 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1208 psz_unicode = psz_unicode_orig =
1209 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1210 if( psz_unicode == NULL )
1212 #if defined(WORDS_BIGENDIAN)
1213 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1215 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1217 if( iconv_handle == (vlc_iconv_t)-1 )
1219 msg_Warn( p_filter, "unable to do conversion" );
1225 const char *p_in_buffer = psz_string;
1226 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1227 i_in_bytes = strlen( psz_string );
1228 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1229 i_out_bytes_left = i_out_bytes;
1230 p_out_buffer = (char *)psz_unicode;
1231 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1233 &p_out_buffer, &i_out_bytes_left );
1235 vlc_iconv_close( iconv_handle );
1239 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1240 "bytes left %u", (unsigned)i_in_bytes );
1243 *(uint32_t*)p_out_buffer = 0;
1244 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1247 #if defined(HAVE_FRIBIDI)
1249 uint32_t *p_fribidi_string;
1250 int32_t start_pos, pos = 0;
1252 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1253 if( !p_fribidi_string )
1256 /* Do bidi conversion line-by-line */
1257 while( pos < i_string_length )
1259 while( pos < i_string_length )
1261 i_char = psz_unicode[pos];
1262 if (i_char != '\r' && i_char != '\n')
1264 p_fribidi_string[pos] = i_char;
1268 while( pos < i_string_length )
1270 i_char = psz_unicode[pos];
1271 if (i_char == '\r' || i_char == '\n')
1275 if (pos > start_pos)
1277 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1278 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1281 (FriBidiChar*)p_fribidi_string + start_pos,
1286 free( psz_unicode_orig );
1287 psz_unicode = psz_unicode_orig = p_fribidi_string;
1288 p_fribidi_string[ i_string_length ] = 0;
1292 /* Calculate relative glyph positions and a bounding box for the
1294 if( !(p_line = NewLine( strlen( psz_string ))) )
1297 i_pen_x = i_pen_y = 0;
1299 psz_line_start = psz_unicode;
1301 #define face p_sys->p_face
1302 #define glyph face->glyph
1304 while( *psz_unicode )
1306 i_char = *psz_unicode++;
1307 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1312 if( i_char == '\n' )
1314 psz_line_start = psz_unicode;
1315 if( !(p_next = NewLine( strlen( psz_string ))) )
1317 p_line->p_next = p_next;
1318 p_line->i_width = line.xMax;
1319 p_line->i_height = face->size->metrics.height >> 6;
1320 p_line->pp_glyphs[ i ] = NULL;
1321 p_line->i_alpha = i_font_alpha;
1322 p_line->i_red = i_red;
1323 p_line->i_green = i_green;
1324 p_line->i_blue = i_blue;
1327 result.x = __MAX( result.x, line.xMax );
1328 result.y += face->size->metrics.height >> 6;
1331 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1332 i_pen_y += face->size->metrics.height >> 6;
1334 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1339 i_glyph_index = FT_Get_Char_Index( face, i_char );
1340 if( p_sys->i_use_kerning && i_glyph_index
1344 FT_Get_Kerning( face, i_previous, i_glyph_index,
1345 ft_kerning_default, &delta );
1346 i_pen_x += delta.x >> 6;
1349 p_line->p_glyph_pos[ i ].x = i_pen_x;
1350 p_line->p_glyph_pos[ i ].y = i_pen_y;
1351 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1354 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1358 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1361 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1365 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1366 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1369 FT_Done_Glyph( tmp_glyph );
1372 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1375 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1376 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1377 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1379 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1380 p_line->pp_glyphs[ i ] = NULL;
1382 p_line = NewLine( strlen( psz_string ));
1383 if( p_prev ) p_prev->p_next = p_line;
1384 else p_lines = p_line;
1386 uint32_t *psz_unicode_saved = psz_unicode;
1387 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1391 if( psz_unicode == psz_line_start )
1392 { /* try harder to break that line */
1393 psz_unicode = psz_unicode_saved;
1394 while( psz_unicode > psz_line_start &&
1395 *psz_unicode != '_' && *psz_unicode != '/' &&
1396 *psz_unicode != '\\' && *psz_unicode != '.' )
1401 if( psz_unicode == psz_line_start )
1403 msg_Warn( p_filter, "unbreakable string" );
1408 *psz_unicode = '\n';
1410 psz_unicode = psz_line_start;
1413 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1416 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1417 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1419 i_previous = i_glyph_index;
1420 i_pen_x += glyph->advance.x >> 6;
1424 p_line->i_width = line.xMax;
1425 p_line->i_height = face->size->metrics.height >> 6;
1426 p_line->pp_glyphs[ i ] = NULL;
1427 p_line->i_alpha = i_font_alpha;
1428 p_line->i_red = i_red;
1429 p_line->i_green = i_green;
1430 p_line->i_blue = i_blue;
1431 result.x = __MAX( result.x, line.xMax );
1432 result.y += line.yMax - line.yMin;
1437 p_region_out->i_x = p_region_in->i_x;
1438 p_region_out->i_y = p_region_in->i_y;
1440 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1441 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1443 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1445 free( psz_unicode_orig );
1446 FreeLines( p_lines );
1450 free( psz_unicode_orig );
1451 FreeLines( p_lines );
1452 return VLC_EGENERIC;
1455 #ifdef HAVE_FONTCONFIG
1456 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1457 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1458 bool b_italic, bool b_uline )
1460 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1464 p_style->i_font_size = i_font_size;
1465 p_style->i_font_color = i_font_color;
1466 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1467 p_style->b_italic = b_italic;
1468 p_style->b_bold = b_bold;
1469 p_style->b_underline = b_uline;
1471 p_style->psz_fontname = strdup( psz_fontname );
1476 static void DeleteStyle( ft_style_t *p_style )
1480 free( p_style->psz_fontname );
1485 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1492 if(( s1->i_font_size == s2->i_font_size ) &&
1493 ( s1->i_font_color == s2->i_font_color ) &&
1494 ( s1->b_italic == s2->b_italic ) &&
1495 ( s1->b_bold == s2->b_bold ) &&
1496 ( s1->b_underline == s2->b_underline ) &&
1497 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1504 static void IconvText( filter_t *p_filter, const char *psz_string,
1505 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1507 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1509 /* If memory hasn't been allocated for our output string, allocate it here
1510 * - the calling function must now be responsible for freeing it.
1512 if( !*ppsz_unicode )
1513 *ppsz_unicode = (uint32_t *)
1514 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1516 /* We don't need to handle a NULL pointer in *ppsz_unicode
1517 * if we are instead testing for a non NULL value like we are here */
1521 #if defined(WORDS_BIGENDIAN)
1522 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1524 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1526 if( iconv_handle != (vlc_iconv_t)-1 )
1528 char *p_in_buffer, *p_out_buffer;
1529 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1530 i_in_bytes = strlen( psz_string );
1531 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1532 i_out_bytes_left = i_out_bytes;
1533 p_in_buffer = (char *) psz_string;
1534 p_out_buffer = (char *) *ppsz_unicode;
1535 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1536 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1538 vlc_iconv_close( iconv_handle );
1542 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1543 "bytes left %u", (unsigned)i_in_bytes );
1547 *(uint32_t*)p_out_buffer = 0;
1549 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1554 msg_Warn( p_filter, "unable to do conversion" );
1559 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1560 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1563 ft_style_t *p_style = NULL;
1565 char *psz_fontname = NULL;
1566 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1567 uint32_t i_karaoke_bg_color = i_font_color;
1568 int i_font_size = p_sys->i_font_size;
1570 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1571 &i_font_color, &i_karaoke_bg_color ))
1573 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1574 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1579 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1580 bool b_uline, int i_karaoke_bgcolor,
1581 line_desc_t *p_line, uint32_t *psz_unicode,
1582 int *pi_pen_x, int i_pen_y, int *pi_start,
1583 FT_Vector *p_result )
1588 bool b_first_on_line = true;
1591 int i_pen_x_start = *pi_pen_x;
1593 uint32_t *psz_unicode_start = psz_unicode;
1595 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1597 /* Account for part of line already in position */
1598 for( i=0; i<*pi_start; i++ )
1602 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1603 ft_glyph_bbox_pixels, &glyph_size );
1605 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1606 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1607 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1608 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1614 b_first_on_line = false;
1616 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1622 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1623 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1627 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1628 ft_kerning_default, &delta );
1629 *pi_pen_x += delta.x >> 6;
1631 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1632 p_line->p_glyph_pos[ i ].y = i_pen_y;
1634 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1638 "unable to render text FT_Load_Glyph returned %d", i_error );
1639 p_line->pp_glyphs[ i ] = NULL;
1640 return VLC_EGENERIC;
1642 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1646 "unable to render text FT_Get_Glyph returned %d", i_error );
1647 p_line->pp_glyphs[ i ] = NULL;
1648 return VLC_EGENERIC;
1650 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1651 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1654 FT_Done_Glyph( tmp_glyph );
1659 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1660 p_face->size->metrics.y_scale));
1661 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1662 p_face->size->metrics.y_scale));
1664 p_line->pi_underline_offset[ i ] =
1665 ( aOffset < 0 ) ? -aOffset : aOffset;
1666 p_line->pi_underline_thickness[ i ] =
1667 ( aSize < 0 ) ? -aSize : aSize;
1669 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1670 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1671 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1672 p_line->p_fg_bg_ratio[ i ] = 0x00;
1674 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1675 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1676 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1678 for( ; i >= *pi_start; i-- )
1679 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1682 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1686 if( psz_unicode == psz_unicode_start )
1688 if( b_first_on_line )
1690 msg_Warn( p_filter, "unbreakable string" );
1691 p_line->pp_glyphs[ i ] = NULL;
1692 return VLC_EGENERIC;
1694 *pi_pen_x = i_pen_x_start;
1696 p_line->i_width = line.xMax;
1697 p_line->i_height = __MAX( p_line->i_height,
1698 p_face->size->metrics.height >> 6 );
1699 p_line->pp_glyphs[ i ] = NULL;
1701 p_result->x = __MAX( p_result->x, line.xMax );
1702 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1703 i_yMax - i_yMin ) );
1708 *psz_unicode = '\n';
1710 psz_unicode = psz_unicode_start;
1711 *pi_pen_x = i_pen_x_start;
1719 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1720 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1722 i_previous = i_glyph_index;
1723 *pi_pen_x += p_face->glyph->advance.x >> 6;
1726 p_line->i_width = line.xMax;
1727 p_line->i_height = __MAX( p_line->i_height,
1728 p_face->size->metrics.height >> 6 );
1729 p_line->pp_glyphs[ i ] = NULL;
1731 p_result->x = __MAX( p_result->x, line.xMax );
1732 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1733 line.yMax - line.yMin ) );
1737 /* Get rid of any text processed - if necessary repositioning
1738 * at the start of a new line of text
1742 *psz_unicode_start = '\0';
1744 else if( psz_unicode > psz_unicode_start )
1746 for( i=0; psz_unicode[ i ]; i++ )
1747 psz_unicode_start[ i ] = psz_unicode[ i ];
1748 psz_unicode_start[ i ] = '\0';
1754 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1755 uint32_t **psz_text_out, uint32_t *pi_runs,
1756 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1757 ft_style_t *p_style )
1759 uint32_t i_string_length = 0;
1761 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1762 *psz_text_out += i_string_length;
1764 if( ppp_styles && ppi_run_lengths )
1770 *ppp_styles = (ft_style_t **)
1771 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1773 else if( *pi_runs == 1 )
1775 *ppp_styles = (ft_style_t **)
1776 malloc( *pi_runs * sizeof( ft_style_t * ) );
1779 /* We have just malloc'ed this memory successfully -
1780 * *pi_runs HAS to be within the memory area of *ppp_styles */
1783 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1787 if( *ppi_run_lengths )
1789 *ppi_run_lengths = (uint32_t *)
1790 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1792 else if( *pi_runs == 1 )
1794 *ppi_run_lengths = (uint32_t *)
1795 malloc( *pi_runs * sizeof( uint32_t ) );
1798 /* same remarks here */
1799 if( *ppi_run_lengths )
1801 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1804 /* If we couldn't use the p_style argument due to memory allocation
1805 * problems above, release it here.
1807 if( p_style ) DeleteStyle( p_style );
1810 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1814 for( k=0; k < p_sys->i_font_attachments; k++ )
1816 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1818 FT_Face p_face = NULL;
1820 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1828 bool match = !strcasecmp( p_face->family_name,
1829 p_style->psz_fontname );
1831 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1832 match = match && p_style->b_bold;
1834 match = match && !p_style->b_bold;
1836 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1837 match = match && p_style->b_italic;
1839 match = match && !p_style->b_italic;
1847 FT_Done_Face( p_face );
1852 return VLC_EGENERIC;
1855 static int ProcessLines( filter_t *p_filter,
1860 uint32_t *pi_run_lengths,
1861 ft_style_t **pp_styles,
1862 line_desc_t **pp_lines,
1864 FT_Vector *p_result,
1868 uint32_t *pi_k_run_lengths,
1869 uint32_t *pi_k_durations )
1871 filter_sys_t *p_sys = p_filter->p_sys;
1872 ft_style_t **pp_char_styles;
1873 int *p_new_positions = NULL;
1874 int8_t *p_levels = NULL;
1875 uint8_t *pi_karaoke_bar = NULL;
1879 /* Assign each character in the text string its style explicitly, so that
1880 * after the characters have been shuffled around by Fribidi, we can re-apply
1881 * the styles, and to simplify the calculation of runs within a line.
1883 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1884 if( !pp_char_styles )
1889 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1890 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1891 * we just won't be able to display the progress bar; at least we'll
1897 for( j = 0; j < i_runs; j++ )
1898 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1899 pp_char_styles[ i++ ] = pp_styles[ j ];
1901 #if defined(HAVE_FRIBIDI)
1903 ft_style_t **pp_char_styles_new;
1904 int *p_old_positions;
1905 uint32_t *p_fribidi_string;
1906 int start_pos, pos = 0;
1908 pp_char_styles_new = (ft_style_t **)
1909 malloc( i_len * sizeof( ft_style_t * ));
1911 p_fribidi_string = (uint32_t *)
1912 malloc( (i_len + 1) * sizeof(uint32_t) );
1913 p_old_positions = (int *)
1914 malloc( (i_len + 1) * sizeof( int ) );
1915 p_new_positions = (int *)
1916 malloc( (i_len + 1) * sizeof( int ) );
1917 p_levels = (int8_t *)
1918 malloc( (i_len + 1) * sizeof( int8_t ) );
1920 if( ! pp_char_styles_new ||
1921 ! p_fribidi_string ||
1922 ! p_old_positions ||
1923 ! p_new_positions ||
1927 free( p_old_positions );
1928 free( p_new_positions );
1929 free( p_fribidi_string );
1930 free( pp_char_styles_new );
1931 free( pi_karaoke_bar );
1933 free( pp_char_styles );
1937 /* Do bidi conversion line-by-line */
1940 while(pos < i_len) {
1941 if (psz_text[pos] != '\n')
1943 p_fribidi_string[pos] = psz_text[pos];
1944 pp_char_styles_new[pos] = pp_char_styles[pos];
1945 p_new_positions[pos] = pos;
1950 while(pos < i_len) {
1951 if (psz_text[pos] == '\n')
1955 if (pos > start_pos)
1957 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1958 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1959 pos - start_pos, &base_dir,
1960 (FriBidiChar*)p_fribidi_string + start_pos,
1961 p_new_positions + start_pos,
1963 p_levels + start_pos );
1964 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1966 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1967 p_old_positions[ j - start_pos ] ];
1968 p_new_positions[ j ] += start_pos;
1972 free( p_old_positions );
1973 free( pp_char_styles );
1974 pp_char_styles = pp_char_styles_new;
1975 psz_text = p_fribidi_string;
1976 p_fribidi_string[ i_len ] = 0;
1979 /* Work out the karaoke */
1980 if( pi_karaoke_bar )
1982 int64_t i_last_duration = 0;
1983 int64_t i_duration = 0;
1984 int64_t i_start_pos = 0;
1985 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1987 for( k = 0; k< i_k_runs; k++ )
1989 double fraction = 0.0;
1991 i_duration += pi_k_durations[ k ];
1993 if( i_duration < i_elapsed )
1995 /* Completely finished this run-length -
1996 * let it render normally */
2000 else if( i_elapsed < i_last_duration )
2002 /* Haven't got up to this segment yet -
2003 * render it completely in karaoke BG mode */
2009 /* Partway through this run */
2011 fraction = (double)(i_elapsed - i_last_duration) /
2012 (double)pi_k_durations[ k ];
2014 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2016 double shade = pi_k_run_lengths[ k ] * fraction;
2018 if( p_new_positions )
2019 j = p_new_positions[ i_start_pos + i ];
2021 j = i_start_pos + i;
2023 if( i < (uint32_t)shade )
2024 pi_karaoke_bar[ j ] = 0xff;
2025 else if( (double)i > shade )
2026 pi_karaoke_bar[ j ] = 0x00;
2029 shade -= (int)shade;
2030 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2031 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2035 i_last_duration = i_duration;
2036 i_start_pos += pi_k_run_lengths[ k ];
2040 free( p_new_positions );
2042 FT_Vector tmp_result;
2044 line_desc_t *p_line = NULL;
2045 line_desc_t *p_prev = NULL;
2051 p_result->x = p_result->y = 0;
2052 tmp_result.x = tmp_result.y = 0;
2055 for( k = 0; k <= (uint32_t) i_len; k++ )
2057 if( ( k == (uint32_t) i_len ) ||
2059 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2061 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2063 /* End of the current style run */
2064 FT_Face p_face = NULL;
2067 /* Look for a match amongst our attachments first */
2068 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2072 char *psz_fontfile = NULL;
2074 vlc_mutex_lock( &fb_lock );
2075 if( p_sys->b_fontconfig_ok )
2077 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2078 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2079 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2080 p_style->psz_fontname,
2085 vlc_mutex_unlock( &fb_lock );
2087 if( psz_fontfile && ! *psz_fontfile )
2089 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2090 " so using default font", p_style->psz_fontname,
2091 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2092 (p_style->b_bold ? "(Bold)" :
2093 (p_style->b_italic ? "(Italic)" : ""))) );
2094 free( psz_fontfile );
2095 psz_fontfile = NULL;
2100 if( FT_New_Face( p_sys->p_library,
2101 psz_fontfile, i_idx, &p_face ) )
2103 free( psz_fontfile );
2104 free( pp_char_styles );
2105 #if defined(HAVE_FRIBIDI)
2108 free( pi_karaoke_bar );
2109 return VLC_EGENERIC;
2111 free( psz_fontfile );
2115 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2117 /* We've loaded a font face which is unhelpful for actually
2118 * rendering text - fallback to the default one.
2120 FT_Done_Face( p_face );
2124 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2125 ft_encoding_unicode ) ||
2126 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2127 p_style->i_font_size ) )
2129 if( p_face ) FT_Done_Face( p_face );
2130 free( pp_char_styles );
2131 #if defined(HAVE_FRIBIDI)
2134 free( pi_karaoke_bar );
2135 return VLC_EGENERIC;
2137 p_sys->i_use_kerning =
2138 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2141 uint32_t *psz_unicode = (uint32_t *)
2142 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2145 if( p_face ) FT_Done_Face( p_face );
2146 free( pp_char_styles );
2147 free( psz_unicode );
2148 #if defined(HAVE_FRIBIDI)
2151 free( pi_karaoke_bar );
2154 memcpy( psz_unicode, psz_text + i_prev,
2155 sizeof( uint32_t ) * ( k - i_prev ) );
2156 psz_unicode[ k - i_prev ] = 0;
2157 while( *psz_unicode )
2161 if( !(p_line = NewLine( i_len - i_prev)) )
2163 if( p_face ) FT_Done_Face( p_face );
2164 free( pp_char_styles );
2165 free( psz_unicode );
2166 #if defined(HAVE_FRIBIDI)
2169 free( pi_karaoke_bar );
2172 /* New Color mode only works in YUVA rendering mode --
2173 * (RGB mode has palette constraints on it). We therefore
2174 * need to populate the legacy colour fields also.
2176 p_line->b_new_color_mode = true;
2177 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2178 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2179 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2180 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2181 p_line->p_next = NULL;
2183 i_pen_y += tmp_result.y;
2187 if( p_prev ) p_prev->p_next = p_line;
2188 else *pp_lines = p_line;
2191 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2192 p_style->i_font_color, p_style->b_underline,
2193 p_style->i_karaoke_bg_color,
2194 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2195 &tmp_result ) != VLC_SUCCESS )
2197 if( p_face ) FT_Done_Face( p_face );
2198 free( pp_char_styles );
2199 free( psz_unicode );
2200 #if defined(HAVE_FRIBIDI)
2203 free( pi_karaoke_bar );
2204 return VLC_EGENERIC;
2209 p_result->x = __MAX( p_result->x, tmp_result.x );
2210 p_result->y += tmp_result.y;
2215 if( *psz_unicode == '\n')
2219 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2221 *c_ptr = *(c_ptr+1);
2226 free( psz_unicode );
2227 if( p_face ) FT_Done_Face( p_face );
2231 free( pp_char_styles );
2232 #if defined(HAVE_FRIBIDI)
2237 p_result->x = __MAX( p_result->x, tmp_result.x );
2238 p_result->y += tmp_result.y;
2241 if( pi_karaoke_bar )
2244 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2246 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2248 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2252 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2254 /* 100% BG colour will render faster if we
2255 * instead make it 100% FG colour, so leave
2256 * the ratio alone and copy the value across
2258 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2262 if( pi_karaoke_bar[ i ] & 0x80 )
2264 /* Swap Left and Right sides over for Right aligned
2265 * language text (eg. Arabic, Hebrew)
2267 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2269 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2270 p_line->p_bg_rgb[ k ] = i_tmp;
2272 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2275 /* Jump over the '\n' at the line-end */
2278 free( pi_karaoke_bar );
2284 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2285 subpicture_region_t *p_region_in )
2287 int rv = VLC_SUCCESS;
2288 stream_t *p_sub = NULL;
2289 xml_t *p_xml = NULL;
2290 xml_reader_t *p_xml_reader = NULL;
2292 if( !p_region_in || !p_region_in->psz_html )
2293 return VLC_EGENERIC;
2295 /* Reset the default fontsize in case screen metrics have changed */
2296 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2298 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2299 (uint8_t *) p_region_in->psz_html,
2300 strlen( p_region_in->psz_html ),
2304 p_xml = xml_Create( p_filter );
2307 bool b_karaoke = false;
2309 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2312 /* Look for Root Node */
2313 if( xml_ReaderRead( p_xml_reader ) == 1 )
2315 char *psz_node = xml_ReaderName( p_xml_reader );
2317 if( !strcasecmp( "karaoke", psz_node ) )
2319 /* We're going to have to render the text a number
2320 * of times to show the progress marker on the text.
2322 var_SetBool( p_filter, "text-rerender", true );
2325 else if( !strcasecmp( "text", psz_node ) )
2331 /* Only text and karaoke tags are supported */
2332 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2333 xml_ReaderDelete( p_xml, p_xml_reader );
2334 p_xml_reader = NULL;
2346 uint32_t i_runs = 0;
2347 uint32_t i_k_runs = 0;
2348 uint32_t *pi_run_lengths = NULL;
2349 uint32_t *pi_k_run_lengths = NULL;
2350 uint32_t *pi_k_durations = NULL;
2351 ft_style_t **pp_styles = NULL;
2353 line_desc_t *p_lines = NULL;
2355 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2356 sizeof( uint32_t ) );
2361 rv = ProcessNodes( p_filter, p_xml_reader,
2362 p_region_in->p_style, psz_text, &i_len,
2363 &i_runs, &pi_run_lengths, &pp_styles,
2365 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2368 p_region_out->i_x = p_region_in->i_x;
2369 p_region_out->i_y = p_region_in->i_y;
2371 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2373 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2374 pi_run_lengths, pp_styles, &p_lines, &result,
2375 b_karaoke, i_k_runs, pi_k_run_lengths,
2379 for( k=0; k<i_runs; k++)
2380 DeleteStyle( pp_styles[k] );
2382 free( pi_run_lengths );
2385 /* Don't attempt to render text that couldn't be layed out
2388 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2390 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2392 Render( p_filter, p_region_out, p_lines,
2393 result.x, result.y );
2397 RenderYUVA( p_filter, p_region_out, p_lines,
2398 result.x, result.y );
2402 FreeLines( p_lines );
2404 xml_ReaderDelete( p_xml, p_xml_reader );
2406 xml_Delete( p_xml );
2408 stream_Delete( p_sub );
2414 static char* FontConfig_Select( FcConfig* priv, const char* family,
2415 bool b_bold, bool b_italic, int *i_idx )
2418 FcPattern *pat, *p_pat;
2422 pat = FcPatternCreate();
2423 if (!pat) return NULL;
2425 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2426 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2427 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2428 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2430 FcDefaultSubstitute( pat );
2432 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2434 FcPatternDestroy( pat );
2438 p_pat = FcFontMatch( priv, pat, &result );
2439 FcPatternDestroy( pat );
2440 if( !p_pat ) return NULL;
2442 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2443 || ( val_b != FcTrue ) )
2445 FcPatternDestroy( p_pat );
2448 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2453 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2455 FcPatternDestroy( p_pat );
2460 if( strcasecmp((const char*)val_s, family ) != 0 )
2461 msg_Warn( p_filter, "fontconfig: selected font family is not"
2462 "the requested one: '%s' != '%s'\n",
2463 (const char*)val_s, family );
2466 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2468 FcPatternDestroy( p_pat );
2472 FcPatternDestroy( p_pat );
2473 return strdup( (const char*)val_s );
2477 static void FreeLine( line_desc_t *p_line )
2480 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2482 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2484 free( p_line->pp_glyphs );
2485 free( p_line->p_glyph_pos );
2486 free( p_line->p_fg_rgb );
2487 free( p_line->p_bg_rgb );
2488 free( p_line->p_fg_bg_ratio );
2489 free( p_line->pi_underline_offset );
2490 free( p_line->pi_underline_thickness );
2494 static void FreeLines( line_desc_t *p_lines )
2496 line_desc_t *p_line, *p_next;
2498 if( !p_lines ) return;
2500 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2502 p_next = p_line->p_next;
2507 static line_desc_t *NewLine( int i_count )
2509 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2511 if( !p_line ) return NULL;
2512 p_line->i_height = 0;
2513 p_line->i_width = 0;
2514 p_line->p_next = NULL;
2516 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2517 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2518 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2519 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2520 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2521 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2522 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2523 if( ( p_line->pp_glyphs == NULL ) ||
2524 ( p_line->p_glyph_pos == NULL ) ||
2525 ( p_line->p_fg_rgb == NULL ) ||
2526 ( p_line->p_bg_rgb == NULL ) ||
2527 ( p_line->p_fg_bg_ratio == NULL ) ||
2528 ( p_line->pi_underline_offset == NULL ) ||
2529 ( p_line->pi_underline_thickness == NULL ) )
2531 free( p_line->pi_underline_thickness );
2532 free( p_line->pi_underline_offset );
2533 free( p_line->p_fg_rgb );
2534 free( p_line->p_bg_rgb );
2535 free( p_line->p_fg_bg_ratio );
2536 free( p_line->p_glyph_pos );
2537 free( p_line->pp_glyphs );
2541 p_line->pp_glyphs[0] = NULL;
2542 p_line->b_new_color_mode = false;
2547 static int GetFontSize( filter_t *p_filter )
2549 filter_sys_t *p_sys = p_filter->p_sys;
2553 if( p_sys->i_default_font_size )
2555 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2556 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2558 i_size = p_sys->i_default_font_size;
2562 var_Get( p_filter, "freetype-rel-fontsize", &val );
2565 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2566 p_filter->p_sys->i_display_height =
2567 p_filter->fmt_out.video.i_height;
2572 msg_Warn( p_filter, "invalid fontsize, using 12" );
2573 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2574 i_size = 12 * val.i_int / 1000;
2581 static int SetFontSize( filter_t *p_filter, int i_size )
2583 filter_sys_t *p_sys = p_filter->p_sys;
2587 i_size = GetFontSize( p_filter );
2589 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2592 p_sys->i_font_size = i_size;
2594 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2596 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2597 return VLC_EGENERIC;
2603 static void YUVFromRGB( uint32_t i_argb,
2604 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2606 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2607 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2608 int i_blue = ( i_argb & 0x000000ff );
2610 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2611 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2612 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2613 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2614 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2615 -585 * i_blue + 4096 + 1048576) >> 13, 240);