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>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
42 #include <vlc_input.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
52 #define FT_MulFix(v, s) (((v)*(s))>>16)
55 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
56 #define FC_DEFAULT_FONT "Lucida Grande"
57 #elif defined( SYS_BEOS )
58 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
59 #define FC_DEFAULT_FONT "Swiss"
60 #elif defined( WIN32 )
61 #define DEFAULT_FONT "" /* Default font found at run-time */
62 #define FC_DEFAULT_FONT "Arial"
64 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
65 #define FC_DEFAULT_FONT "Serif Bold"
68 #if defined(HAVE_FRIBIDI)
69 #include <fribidi/fribidi.h>
72 #ifdef HAVE_FONTCONFIG
73 #include <fontconfig/fontconfig.h>
78 typedef struct line_desc_t line_desc_t;
80 /*****************************************************************************
82 *****************************************************************************/
83 static int Create ( vlc_object_t * );
84 static void Destroy( vlc_object_t * );
86 static int LoadFontsFromAttachments( filter_t *p_filter );
88 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
89 static int RenderText( filter_t *, subpicture_region_t *,
90 subpicture_region_t * );
91 #ifdef HAVE_FONTCONFIG
92 static int RenderHtml( filter_t *, subpicture_region_t *,
93 subpicture_region_t * );
94 static char *FontConfig_Select( FcConfig *, const char *,
96 static int BuildDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
99 static line_desc_t *NewLine( int );
101 static int GetFontSize( filter_t *p_filter );
102 static int SetFontSize( filter_t *, int );
103 static void YUVFromRGB( uint32_t i_argb,
104 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
106 /*****************************************************************************
108 *****************************************************************************/
109 #define FONT_TEXT N_("Font")
110 #define FONT_LONGTEXT N_("Filename for the font you want to use")
111 #define FONTSIZE_TEXT N_("Font size in pixels")
112 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
113 "that will be rendered on the video. " \
114 "If set to something different than 0 this option will override the " \
115 "relative font size." )
116 #define OPACITY_TEXT N_("Opacity")
117 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
118 "text that will be rendered on the video. 0 = transparent, " \
119 "255 = totally opaque. " )
120 #define COLOR_TEXT N_("Text default color")
121 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
122 "the video. This must be an hexadecimal (like HTML colors). The first two "\
123 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
124 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
125 #define FONTSIZER_TEXT N_("Relative font size")
126 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
127 "fonts that will be rendered on the video. If absolute font size is set, "\
128 "relative size will be overriden." )
130 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
131 static const char *const ppsz_sizes_text[] = {
132 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
133 #define YUVP_TEXT N_("Use YUVP renderer")
134 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
135 "This option is only needed if you want to encode into DVB subtitles" )
136 #define EFFECT_TEXT N_("Font Effect")
137 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
138 "text to improve its readability." )
140 #define EFFECT_BACKGROUND 1
141 #define EFFECT_OUTLINE 2
142 #define EFFECT_OUTLINE_FAT 3
144 static int const pi_effects[] = { 1, 2, 3 };
145 static const char *const ppsz_effects_text[] = {
146 N_("Background"),N_("Outline"), N_("Fat Outline") };
147 static const int pi_color_values[] = {
148 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
149 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
150 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
152 static const char *const ppsz_color_descriptions[] = {
153 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
154 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
155 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
158 set_shortname( N_("Text renderer"));
159 set_description( N_("Freetype2 font renderer") );
160 set_category( CAT_VIDEO );
161 set_subcategory( SUBCAT_VIDEO_SUBPIC );
163 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
166 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
167 FONTSIZE_LONGTEXT, true );
169 /* opacity valid on 0..255, with default 255 = fully opaque */
170 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
171 OPACITY_TEXT, OPACITY_LONGTEXT, true );
173 /* hook to the color values list, with default 0x00ffffff = white */
174 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
175 COLOR_LONGTEXT, false );
176 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
178 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
179 FONTSIZER_LONGTEXT, false );
180 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
181 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
182 EFFECT_LONGTEXT, false );
183 change_integer_list( pi_effects, ppsz_effects_text, 0 );
185 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
186 YUVP_LONGTEXT, true );
187 set_capability( "text renderer", 100 );
188 add_shortcut( "text" );
189 set_callbacks( Create, Destroy );
194 /** NULL-terminated list of glyphs making the string */
195 FT_BitmapGlyph *pp_glyphs;
196 /** list of relative positions for the glyphs */
197 FT_Vector *p_glyph_pos;
198 /** list of RGB information for styled text
199 * -- if the rendering mode supports it (RenderYUVA) and
200 * b_new_color_mode is set, then it becomes possible to
201 * have multicoloured text within the subtitles. */
204 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
205 bool b_new_color_mode;
206 /** underline information -- only supplied if text should be underlined */
207 uint16_t *pi_underline_offset;
208 uint16_t *pi_underline_thickness;
212 int i_red, i_green, i_blue;
218 typedef struct font_stack_t font_stack_t;
223 uint32_t i_color; /* ARGB */
224 uint32_t i_karaoke_bg_color; /* ARGB */
226 font_stack_t *p_next;
232 uint32_t i_font_color; /* ARGB */
233 uint32_t i_karaoke_bg_color; /* ARGB */
240 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
241 static void FreeLines( line_desc_t * );
242 static void FreeLine( line_desc_t * );
243 #ifdef HAVE_FONTCONFIG
244 static void FontBuilder( vlc_object_t *p_this);
247 /*****************************************************************************
248 * filter_sys_t: freetype local data
249 *****************************************************************************
250 * This structure is part of the video output thread descriptor.
251 * It describes the freetype specific properties of an output thread.
252 *****************************************************************************/
255 FT_Library p_library; /* handle to library */
256 FT_Face p_face; /* handle to face object */
258 uint8_t i_font_opacity;
263 int i_default_font_size;
264 int i_display_height;
265 #ifdef HAVE_FONTCONFIG
266 FcConfig *p_fontconfig;
267 bool b_fontconfig_ok;
268 vlc_mutex_t fontconfig_lock;
271 input_attachment_t **pp_font_attachments;
272 int i_font_attachments;
275 /*****************************************************************************
276 * Create: allocates osd-text video thread output method
277 *****************************************************************************
278 * This function allocates and initializes a Clone vout method.
279 *****************************************************************************/
280 static int Create( vlc_object_t *p_this )
282 filter_t *p_filter = (filter_t *)p_this;
284 char *psz_fontfile = NULL;
287 vlc_object_t *p_fontbuilder;
289 /* Allocate structure */
290 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
294 p_sys->p_library = 0;
295 p_sys->i_font_size = 0;
296 p_sys->i_display_height = 0;
298 var_Create( p_filter, "freetype-font",
299 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
300 var_Create( p_filter, "freetype-fontsize",
301 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
302 var_Create( p_filter, "freetype-rel-fontsize",
303 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
304 var_Create( p_filter, "freetype-opacity",
305 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
306 var_Create( p_filter, "freetype-effect",
307 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
308 var_Get( p_filter, "freetype-opacity", &val );
309 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
310 var_Create( p_filter, "freetype-color",
311 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
312 var_Get( p_filter, "freetype-color", &val );
313 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
314 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
316 /* Look what method was requested */
317 var_Get( p_filter, "freetype-font", &val );
318 psz_fontfile = val.psz_string;
319 if( !psz_fontfile || !*psz_fontfile )
321 free( psz_fontfile );
322 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
326 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
327 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
328 #elif defined(__APPLE__)
329 strcpy( psz_fontfile, DEFAULT_FONT );
331 msg_Err( p_filter, "user didn't specify a font" );
336 i_error = FT_Init_FreeType( &p_sys->p_library );
339 msg_Err( p_filter, "couldn't initialize freetype" );
342 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
344 if( i_error == FT_Err_Unknown_File_Format )
346 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
351 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
355 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
358 msg_Err( p_filter, "font has no unicode translation table" );
362 #ifdef HAVE_FONTCONFIG
363 vlc_mutex_init( &p_sys->fontconfig_lock );
364 p_sys->b_fontconfig_ok = false;
365 p_sys->p_fontconfig = NULL;
367 /* Check for an existing Fontbuilder thread */
368 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
369 p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
373 if( ! p_fontbuilder )
375 /* Create the FontBuilder thread as a child of a top-level
376 * object, so that it can survive the destruction of the
377 * freetype object - the fontlist only needs to be built once,
378 * and calling the fontbuild a second time while the first is
379 * still in progress can cause thread instabilities.
382 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
383 sizeof(vlc_object_t) );
386 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
387 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
389 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
390 var_SetBool( p_fontbuilder, "build-done", false );
391 var_AddCallback( p_fontbuilder, "build-done", BuildDone, p_sys );
393 if( vlc_thread_create( p_fontbuilder,
396 VLC_THREAD_PRIORITY_LOW,
399 /* Don't destroy the fontconfig object - we won't be able to do
400 * italics or bold or change the font face, but we will still
401 * be able to do underline and change the font size.
403 msg_Warn( p_filter, "fontconfig database builder thread can't "
404 "be launched. Font styling support will be limited." );
409 vlc_object_release( p_fontbuilder );
414 vlc_object_release( p_fontbuilder );
416 vlc_mutex_unlock( lock );
420 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
422 var_Get( p_filter, "freetype-fontsize", &val );
423 p_sys->i_default_font_size = val.i_int;
424 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
426 free( psz_fontfile );
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;
435 p_filter->pf_render_html = NULL;
438 LoadFontsFromAttachments( p_filter );
443 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
444 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
445 free( psz_fontfile );
450 /*****************************************************************************
451 * Destroy: destroy Clone video thread output method
452 *****************************************************************************
453 * Clean up all data and library connections
454 *****************************************************************************/
455 static void Destroy( vlc_object_t *p_this )
457 filter_t *p_filter = (filter_t *)p_this;
458 filter_sys_t *p_sys = p_filter->p_sys;
460 if( p_sys->pp_font_attachments )
464 for( k = 0; k < p_sys->i_font_attachments; k++ )
466 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
469 free( p_sys->pp_font_attachments );
472 #ifdef HAVE_FONTCONFIG
473 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
474 vlc_object_t *p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
475 "fontlist builder", FIND_CHILD );
478 var_DelCallback( p_fontbuilder, "build-done", BuildDone, p_sys );
479 vlc_object_release( p_fontbuilder );
481 vlc_mutex_unlock( lock );
483 vlc_mutex_destroy( &p_sys->fontconfig_lock );
485 if( p_sys->p_fontconfig )
487 FcConfigDestroy( p_sys->p_fontconfig );
488 p_sys->p_fontconfig = NULL;
490 /* FcFini asserts calling the subfunction FcCacheFini()
491 * even if no other library functions have been made since FcInit(),
492 * so don't call it. */
494 FT_Done_Face( p_sys->p_face );
495 FT_Done_FreeType( p_sys->p_library );
499 #ifdef HAVE_FONTCONFIG
501 static void FontBuilder( vlc_object_t *p_this )
503 FcConfig *p_fontconfig = FcInitLoadConfig();
506 vlc_thread_ready( p_this );
512 msg_Dbg( p_this, "Building font database..." );
514 if(! FcConfigBuildFonts( p_fontconfig ))
516 /* Don't destroy the fontconfig object - we won't be able to do
517 * italics or bold or change the font face, but we will still
518 * be able to do underline and change the font size.
520 msg_Err( p_this, "fontconfig database can't be built. "
521 "Font styling won't be available" );
525 msg_Dbg( p_this, "Finished building font database." );
526 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
528 lock = var_AcquireMutex( "fontbuilder" );
530 var_SetBool( p_this, "build-done", true );
532 FcConfigDestroy( p_fontconfig );
533 vlc_mutex_unlock( lock );
535 vlc_object_detach( p_this );
536 vlc_object_release( p_this );
541 /*****************************************************************************
542 * Make any TTF/OTF fonts present in the attachments of the media file
543 * and store them for later use by the FreeType Engine
544 *****************************************************************************/
545 static int LoadFontsFromAttachments( filter_t *p_filter )
547 filter_sys_t *p_sys = p_filter->p_sys;
548 input_thread_t *p_input;
549 input_attachment_t **pp_attachments;
550 int i_attachments_cnt;
552 int rv = VLC_SUCCESS;
554 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
558 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
560 vlc_object_release(p_input);
564 p_sys->i_font_attachments = 0;
565 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
566 if(! p_sys->pp_font_attachments )
569 for( k = 0; k < i_attachments_cnt; k++ )
571 input_attachment_t *p_attach = pp_attachments[k];
573 if( p_sys->pp_font_attachments )
575 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
576 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
577 ( p_attach->i_data > 0 ) &&
578 ( p_attach->p_data != NULL ) )
580 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
584 vlc_input_attachment_Delete( p_attach );
589 vlc_input_attachment_Delete( p_attach );
592 free( pp_attachments );
594 vlc_object_release(p_input);
599 /*****************************************************************************
600 * Render: place string in picture
601 *****************************************************************************
602 * This function merges the previously rendered freetype glyphs into a picture
603 *****************************************************************************/
604 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
605 line_desc_t *p_line, int i_width, int i_height )
607 static uint8_t pi_gamma[16] =
608 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
609 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
613 int i, x, y, i_pitch;
614 uint8_t i_y; /* YUV values, derived from incoming RGB */
616 subpicture_region_t *p_region_tmp;
618 /* Create a new subpicture region */
619 memset( &fmt, 0, sizeof(video_format_t) );
620 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
622 fmt.i_width = fmt.i_visible_width = i_width + 4;
623 fmt.i_height = fmt.i_visible_height = i_height + 4;
624 if( p_region->fmt.i_visible_width > 0 )
625 fmt.i_visible_width = p_region->fmt.i_visible_width;
626 if( p_region->fmt.i_visible_height > 0 )
627 fmt.i_visible_height = p_region->fmt.i_visible_height;
628 fmt.i_x_offset = fmt.i_y_offset = 0;
629 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
632 msg_Err( p_filter, "cannot allocate SPU region" );
636 p_region->fmt = p_region_tmp->fmt;
637 p_region->picture = p_region_tmp->picture;
638 free( p_region_tmp );
640 /* Calculate text color components */
641 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
642 25 * p_line->i_blue + 128) >> 8) + 16;
643 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
644 112 * p_line->i_blue + 128) >> 8) + 128;
645 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
646 18 * p_line->i_blue + 128) >> 8) + 128;
649 fmt.p_palette->i_entries = 16;
650 for( i = 0; i < 8; i++ )
652 fmt.p_palette->palette[i][0] = 0;
653 fmt.p_palette->palette[i][1] = 0x80;
654 fmt.p_palette->palette[i][2] = 0x80;
655 fmt.p_palette->palette[i][3] = pi_gamma[i];
656 fmt.p_palette->palette[i][3] =
657 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
659 for( i = 8; i < fmt.p_palette->i_entries; i++ )
661 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
662 fmt.p_palette->palette[i][1] = i_u;
663 fmt.p_palette->palette[i][2] = i_v;
664 fmt.p_palette->palette[i][3] = pi_gamma[i];
665 fmt.p_palette->palette[i][3] =
666 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
669 p_dst = p_region->picture.Y_PIXELS;
670 i_pitch = p_region->picture.Y_PITCH;
672 /* Initialize the region pixels */
673 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
675 for( ; p_line != NULL; p_line = p_line->p_next )
677 int i_glyph_tmax = 0;
678 int i_bitmap_offset, i_offset, i_align_offset = 0;
679 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
681 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
682 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
685 if( p_line->i_width < i_width )
687 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
689 i_align_offset = i_width - p_line->i_width;
691 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
693 i_align_offset = ( i_width - p_line->i_width ) / 2;
697 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
699 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
701 i_offset = ( p_line->p_glyph_pos[ i ].y +
702 i_glyph_tmax - p_glyph->top + 2 ) *
703 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
706 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
708 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
710 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
712 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
719 /* Outlining (find something better than nearest neighbour filtering ?) */
722 uint8_t *p_dst = p_region->picture.Y_PIXELS;
723 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
724 uint8_t left, current;
726 for( y = 1; y < (int)fmt.i_height - 1; y++ )
728 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
729 p_dst += p_region->picture.Y_PITCH;
732 for( x = 1; x < (int)fmt.i_width - 1; x++ )
735 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
736 p_dst[x -1 + p_region->picture.Y_PITCH ] + p_dst[x + p_region->picture.Y_PITCH] + p_dst[x + 1 + p_region->picture.Y_PITCH]) / 16;
740 memset( p_top, 0, fmt.i_width );
746 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
747 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
748 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
749 int i_glyph_tmax, int i_align_offset,
750 uint8_t i_y, uint8_t i_u, uint8_t i_v,
751 subpicture_region_t *p_region)
755 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
757 p_dst_y = p_region->picture.Y_PIXELS;
758 p_dst_u = p_region->picture.U_PIXELS;
759 p_dst_v = p_region->picture.V_PIXELS;
760 p_dst_a = p_region->picture.A_PIXELS;
761 i_pitch = p_region->picture.A_PITCH;
763 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
764 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
766 for( y = 0; y < i_line_thickness; y++ )
768 int i_extra = p_this_glyph->bitmap.width;
772 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
773 (p_this_glyph_pos->x + p_this_glyph->left);
775 for( x = 0; x < i_extra; x++ )
779 /* break the underline around the tails of any glyphs which cross it */
780 for( z = x - i_line_thickness;
781 z < x + i_line_thickness && b_ok;
784 if( p_next_glyph && ( z >= i_extra ) )
786 int i_row = i_line_offset + p_next_glyph->top + y;
788 if( ( p_next_glyph->bitmap.rows > i_row ) &&
789 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
794 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
796 int i_row = i_line_offset + p_this_glyph->top + y;
798 if( ( p_this_glyph->bitmap.rows > i_row ) &&
799 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
808 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
809 p_dst_u[i_offset+x] = i_u;
810 p_dst_v[i_offset+x] = i_v;
811 p_dst_a[i_offset+x] = 255;
818 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
820 uint8_t *p_dst = p_region->picture.A_PIXELS;
821 int i_pitch = p_region->picture.A_PITCH;
824 for( ; p_line != NULL; p_line = p_line->p_next )
826 int i_glyph_tmax=0, i = 0;
827 int i_bitmap_offset, i_offset, i_align_offset = 0;
828 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
830 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
831 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
834 if( p_line->i_width < i_width )
836 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
838 i_align_offset = i_width - p_line->i_width;
840 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
842 i_align_offset = ( i_width - p_line->i_width ) / 2;
846 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
848 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
850 i_offset = ( p_line->p_glyph_pos[ i ].y +
851 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
852 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
853 i_align_offset +xoffset;
855 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
857 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
859 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
860 if( p_dst[i_offset+x] <
861 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
863 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
872 /*****************************************************************************
873 * Render: place string in picture
874 *****************************************************************************
875 * This function merges the previously rendered freetype glyphs into a picture
876 *****************************************************************************/
877 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
878 line_desc_t *p_line, int i_width, int i_height )
880 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
882 int i, x, y, i_pitch, i_alpha;
883 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
884 subpicture_region_t *p_region_tmp;
886 if( i_width == 0 || i_height == 0 )
889 /* Create a new subpicture region */
890 memset( &fmt, 0, sizeof(video_format_t) );
891 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
893 fmt.i_width = fmt.i_visible_width = i_width + 6;
894 fmt.i_height = fmt.i_visible_height = i_height + 6;
895 if( p_region->fmt.i_visible_width > 0 )
896 fmt.i_visible_width = p_region->fmt.i_visible_width;
897 if( p_region->fmt.i_visible_height > 0 )
898 fmt.i_visible_height = p_region->fmt.i_visible_height;
899 fmt.i_x_offset = fmt.i_y_offset = 0;
900 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
903 msg_Err( p_filter, "cannot allocate SPU region" );
907 p_region->fmt = p_region_tmp->fmt;
908 p_region->picture = p_region_tmp->picture;
909 free( p_region_tmp );
911 /* Calculate text color components */
912 YUVFromRGB( (p_line->i_red << 16) |
913 (p_line->i_green << 8) |
916 i_alpha = p_line->i_alpha;
918 p_dst_y = p_region->picture.Y_PIXELS;
919 p_dst_u = p_region->picture.U_PIXELS;
920 p_dst_v = p_region->picture.V_PIXELS;
921 p_dst_a = p_region->picture.A_PIXELS;
922 i_pitch = p_region->picture.A_PITCH;
924 /* Initialize the region pixels */
925 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
927 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
928 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
929 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
930 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
934 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
935 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
936 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
937 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
939 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
940 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
942 DrawBlack( p_line, i_width, p_region, 0, 0);
943 DrawBlack( p_line, i_width, p_region, -1, 0);
944 DrawBlack( p_line, i_width, p_region, 0, -1);
945 DrawBlack( p_line, i_width, p_region, 1, 0);
946 DrawBlack( p_line, i_width, p_region, 0, 1);
949 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
951 DrawBlack( p_line, i_width, p_region, -1, -1);
952 DrawBlack( p_line, i_width, p_region, -1, 1);
953 DrawBlack( p_line, i_width, p_region, 1, -1);
954 DrawBlack( p_line, i_width, p_region, 1, 1);
956 DrawBlack( p_line, i_width, p_region, -2, 0);
957 DrawBlack( p_line, i_width, p_region, 0, -2);
958 DrawBlack( p_line, i_width, p_region, 2, 0);
959 DrawBlack( p_line, i_width, p_region, 0, 2);
961 DrawBlack( p_line, i_width, p_region, -2, -2);
962 DrawBlack( p_line, i_width, p_region, -2, 2);
963 DrawBlack( p_line, i_width, p_region, 2, -2);
964 DrawBlack( p_line, i_width, p_region, 2, 2);
966 DrawBlack( p_line, i_width, p_region, -3, 0);
967 DrawBlack( p_line, i_width, p_region, 0, -3);
968 DrawBlack( p_line, i_width, p_region, 3, 0);
969 DrawBlack( p_line, i_width, p_region, 0, 3);
972 for( ; p_line != NULL; p_line = p_line->p_next )
974 int i_glyph_tmax = 0;
975 int i_bitmap_offset, i_offset, i_align_offset = 0;
976 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
978 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
979 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
982 if( p_line->i_width < i_width )
984 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
986 i_align_offset = i_width - p_line->i_width;
988 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
990 i_align_offset = ( i_width - p_line->i_width ) / 2;
994 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
996 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
998 i_offset = ( p_line->p_glyph_pos[ i ].y +
999 i_glyph_tmax - p_glyph->top + 3 ) *
1000 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1003 if( p_line->b_new_color_mode )
1005 /* Every glyph can (and in fact must) have its own color */
1006 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1009 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1011 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1013 uint8_t i_y_local = i_y;
1014 uint8_t i_u_local = i_u;
1015 uint8_t i_v_local = i_v;
1017 if( p_line->p_fg_bg_ratio != 0x00 )
1019 int i_split = p_glyph->bitmap.width *
1020 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1024 YUVFromRGB( p_line->p_bg_rgb[ i ],
1025 &i_y_local, &i_u_local, &i_v_local );
1029 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1031 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1032 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1034 p_dst_u[i_offset+x] = i_u;
1035 p_dst_v[i_offset+x] = i_v;
1037 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1038 p_dst_a[i_offset+x] = 0xff;
1041 i_offset += i_pitch;
1044 if( p_line->pi_underline_thickness[ i ] )
1046 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1047 p_line->pi_underline_offset[ i ],
1048 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1049 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1050 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1051 i_glyph_tmax, i_align_offset,
1058 /* Apply the alpha setting */
1059 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1060 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1066 * This function renders a text subpicture region into another one.
1067 * It also calculates the size needed for this string, and renders the
1068 * needed glyphs into memory. It is used as pf_add_string callback in
1069 * the vout method by this module
1071 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1072 subpicture_region_t *p_region_in )
1074 filter_sys_t *p_sys = p_filter->p_sys;
1075 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1076 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1077 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1078 int i_string_length;
1080 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1081 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1091 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1092 psz_string = p_region_in->psz_text;
1093 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1095 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1096 i_scale = val.i_int;
1098 if( p_region_in->p_style )
1100 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1101 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1102 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1106 i_font_color = p_sys->i_font_color;
1107 i_font_alpha = 255 - p_sys->i_font_opacity;
1108 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1111 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1112 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1113 SetFontSize( p_filter, i_font_size );
1115 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1116 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1117 i_blue = i_font_color & 0x000000FF;
1119 result.x = result.y = 0;
1120 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1122 psz_unicode = psz_unicode_orig =
1123 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1124 if( psz_unicode == NULL )
1126 #if defined(WORDS_BIGENDIAN)
1127 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1129 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1131 if( iconv_handle == (vlc_iconv_t)-1 )
1133 msg_Warn( p_filter, "unable to do conversion" );
1139 const char *p_in_buffer = psz_string;
1140 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1141 i_in_bytes = strlen( psz_string );
1142 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1143 i_out_bytes_left = i_out_bytes;
1144 p_out_buffer = (char *)psz_unicode;
1145 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1147 &p_out_buffer, &i_out_bytes_left );
1149 vlc_iconv_close( iconv_handle );
1153 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1154 "bytes left %u", (unsigned)i_in_bytes );
1157 *(uint32_t*)p_out_buffer = 0;
1158 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1161 #if defined(HAVE_FRIBIDI)
1163 uint32_t *p_fribidi_string;
1164 int32_t start_pos, pos = 0;
1166 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1167 if( !p_fribidi_string )
1170 /* Do bidi conversion line-by-line */
1171 while( pos < i_string_length )
1173 while( pos < i_string_length )
1175 i_char = psz_unicode[pos];
1176 if (i_char != '\r' && i_char != '\n')
1178 p_fribidi_string[pos] = i_char;
1182 while( pos < i_string_length )
1184 i_char = psz_unicode[pos];
1185 if (i_char == '\r' || i_char == '\n')
1189 if (pos > start_pos)
1191 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1192 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1195 (FriBidiChar*)p_fribidi_string + start_pos,
1200 free( psz_unicode_orig );
1201 psz_unicode = psz_unicode_orig = p_fribidi_string;
1202 p_fribidi_string[ i_string_length ] = 0;
1206 /* Calculate relative glyph positions and a bounding box for the
1208 if( !(p_line = NewLine( strlen( psz_string ))) )
1211 i_pen_x = i_pen_y = 0;
1213 psz_line_start = psz_unicode;
1215 #define face p_sys->p_face
1216 #define glyph face->glyph
1218 while( *psz_unicode )
1220 i_char = *psz_unicode++;
1221 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1226 if( i_char == '\n' )
1228 psz_line_start = psz_unicode;
1229 if( !(p_next = NewLine( strlen( psz_string ))) )
1231 p_line->p_next = p_next;
1232 p_line->i_width = line.xMax;
1233 p_line->i_height = face->size->metrics.height >> 6;
1234 p_line->pp_glyphs[ i ] = NULL;
1235 p_line->i_alpha = i_font_alpha;
1236 p_line->i_red = i_red;
1237 p_line->i_green = i_green;
1238 p_line->i_blue = i_blue;
1241 result.x = __MAX( result.x, line.xMax );
1242 result.y += face->size->metrics.height >> 6;
1245 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1246 i_pen_y += face->size->metrics.height >> 6;
1248 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1253 i_glyph_index = FT_Get_Char_Index( face, i_char );
1254 if( p_sys->i_use_kerning && i_glyph_index
1258 FT_Get_Kerning( face, i_previous, i_glyph_index,
1259 ft_kerning_default, &delta );
1260 i_pen_x += delta.x >> 6;
1263 p_line->p_glyph_pos[ i ].x = i_pen_x;
1264 p_line->p_glyph_pos[ i ].y = i_pen_y;
1265 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1268 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1272 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1275 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1279 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1280 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1283 FT_Done_Glyph( tmp_glyph );
1286 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1289 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1290 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1291 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1293 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1294 p_line->pp_glyphs[ i ] = NULL;
1296 p_line = NewLine( strlen( psz_string ));
1297 if( p_prev ) p_prev->p_next = p_line;
1298 else p_lines = p_line;
1300 uint32_t *psz_unicode_saved = psz_unicode;
1301 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1305 if( psz_unicode == psz_line_start )
1306 { /* try harder to break that line */
1307 psz_unicode = psz_unicode_saved;
1308 while( psz_unicode > psz_line_start &&
1309 *psz_unicode != '_' && *psz_unicode != '/' &&
1310 *psz_unicode != '\\' && *psz_unicode != '.' )
1315 if( psz_unicode == psz_line_start )
1317 msg_Warn( p_filter, "unbreakable string" );
1322 *psz_unicode = '\n';
1324 psz_unicode = psz_line_start;
1327 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1330 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1331 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1333 i_previous = i_glyph_index;
1334 i_pen_x += glyph->advance.x >> 6;
1338 p_line->i_width = line.xMax;
1339 p_line->i_height = face->size->metrics.height >> 6;
1340 p_line->pp_glyphs[ i ] = NULL;
1341 p_line->i_alpha = i_font_alpha;
1342 p_line->i_red = i_red;
1343 p_line->i_green = i_green;
1344 p_line->i_blue = i_blue;
1345 result.x = __MAX( result.x, line.xMax );
1346 result.y += line.yMax - line.yMin;
1351 p_region_out->i_x = p_region_in->i_x;
1352 p_region_out->i_y = p_region_in->i_y;
1354 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1355 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1357 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1359 free( psz_unicode_orig );
1360 FreeLines( p_lines );
1364 free( psz_unicode_orig );
1365 FreeLines( p_lines );
1366 return VLC_EGENERIC;
1369 #ifdef HAVE_FONTCONFIG
1370 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1371 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1372 bool b_italic, bool b_uline )
1374 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1378 p_style->i_font_size = i_font_size;
1379 p_style->i_font_color = i_font_color;
1380 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1381 p_style->b_italic = b_italic;
1382 p_style->b_bold = b_bold;
1383 p_style->b_underline = b_uline;
1385 p_style->psz_fontname = strdup( psz_fontname );
1390 static void DeleteStyle( ft_style_t *p_style )
1394 free( p_style->psz_fontname );
1399 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1406 if(( s1->i_font_size == s2->i_font_size ) &&
1407 ( s1->i_font_color == s2->i_font_color ) &&
1408 ( s1->b_italic == s2->b_italic ) &&
1409 ( s1->b_bold == s2->b_bold ) &&
1410 ( s1->b_underline == s2->b_underline ) &&
1411 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1418 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1419 uint32_t i_color, uint32_t i_karaoke_bg_color )
1421 font_stack_t *p_new;
1424 return VLC_EGENERIC;
1426 p_new = malloc( sizeof( font_stack_t ) );
1430 p_new->p_next = NULL;
1433 p_new->psz_name = strdup( psz_name );
1435 p_new->psz_name = NULL;
1437 p_new->i_size = i_size;
1438 p_new->i_color = i_color;
1439 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1447 font_stack_t *p_last;
1449 for( p_last = *p_font;
1451 p_last = p_last->p_next )
1454 p_last->p_next = p_new;
1459 static int PopFont( font_stack_t **p_font )
1461 font_stack_t *p_last, *p_next_to_last;
1463 if( !p_font || !*p_font )
1464 return VLC_EGENERIC;
1466 p_next_to_last = NULL;
1467 for( p_last = *p_font;
1469 p_last = p_last->p_next )
1471 p_next_to_last = p_last;
1474 if( p_next_to_last )
1475 p_next_to_last->p_next = NULL;
1479 free( p_last->psz_name );
1485 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1486 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1488 font_stack_t *p_last;
1490 if( !p_font || !*p_font )
1491 return VLC_EGENERIC;
1493 for( p_last=*p_font;
1495 p_last=p_last->p_next )
1498 *psz_name = p_last->psz_name;
1499 *i_size = p_last->i_size;
1500 *i_color = p_last->i_color;
1501 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1506 static void IconvText( filter_t *p_filter, const char *psz_string,
1507 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1509 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1511 /* If memory hasn't been allocated for our output string, allocate it here
1512 * - the calling function must now be responsible for freeing it.
1514 if( !*ppsz_unicode )
1515 *ppsz_unicode = (uint32_t *)
1516 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1518 /* We don't need to handle a NULL pointer in *ppsz_unicode
1519 * if we are instead testing for a non NULL value like we are here */
1523 #if defined(WORDS_BIGENDIAN)
1524 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1526 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1528 if( iconv_handle != (vlc_iconv_t)-1 )
1530 char *p_in_buffer, *p_out_buffer;
1531 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1532 i_in_bytes = strlen( psz_string );
1533 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1534 i_out_bytes_left = i_out_bytes;
1535 p_in_buffer = (char *) psz_string;
1536 p_out_buffer = (char *) *ppsz_unicode;
1537 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1538 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1540 vlc_iconv_close( iconv_handle );
1544 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1545 "bytes left %u", (unsigned)i_in_bytes );
1549 *(uint32_t*)p_out_buffer = 0;
1551 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1556 msg_Warn( p_filter, "unable to do conversion" );
1561 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1562 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1565 ft_style_t *p_style = NULL;
1567 char *psz_fontname = NULL;
1568 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1569 uint32_t i_karaoke_bg_color = i_font_color;
1570 int i_font_size = p_sys->i_font_size;
1572 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1573 &i_font_color, &i_karaoke_bg_color ))
1575 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1576 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1581 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1582 bool b_uline, int i_karaoke_bgcolor,
1583 line_desc_t *p_line, uint32_t *psz_unicode,
1584 int *pi_pen_x, int i_pen_y, int *pi_start,
1585 FT_Vector *p_result )
1590 bool b_first_on_line = true;
1593 int i_pen_x_start = *pi_pen_x;
1595 uint32_t *psz_unicode_start = psz_unicode;
1597 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1599 /* Account for part of line already in position */
1600 for( i=0; i<*pi_start; i++ )
1604 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1605 ft_glyph_bbox_pixels, &glyph_size );
1607 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1608 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1609 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1610 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1616 b_first_on_line = false;
1618 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1624 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1625 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1629 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1630 ft_kerning_default, &delta );
1631 *pi_pen_x += delta.x >> 6;
1633 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1634 p_line->p_glyph_pos[ i ].y = i_pen_y;
1636 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1640 "unable to render text FT_Load_Glyph returned %d", i_error );
1641 p_line->pp_glyphs[ i ] = NULL;
1642 return VLC_EGENERIC;
1644 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1648 "unable to render text FT_Get_Glyph returned %d", i_error );
1649 p_line->pp_glyphs[ i ] = NULL;
1650 return VLC_EGENERIC;
1652 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1653 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1656 FT_Done_Glyph( tmp_glyph );
1661 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1662 p_face->size->metrics.y_scale));
1663 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1664 p_face->size->metrics.y_scale));
1666 p_line->pi_underline_offset[ i ] =
1667 ( aOffset < 0 ) ? -aOffset : aOffset;
1668 p_line->pi_underline_thickness[ i ] =
1669 ( aSize < 0 ) ? -aSize : aSize;
1671 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1672 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1673 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1674 p_line->p_fg_bg_ratio[ i ] = 0x00;
1676 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1677 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1678 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1680 while( --i > *pi_start )
1682 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1685 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1689 if( psz_unicode == psz_unicode_start )
1691 if( b_first_on_line )
1693 msg_Warn( p_filter, "unbreakable string" );
1694 p_line->pp_glyphs[ i ] = NULL;
1695 return VLC_EGENERIC;
1697 *pi_pen_x = i_pen_x_start;
1699 p_line->i_width = line.xMax;
1700 p_line->i_height = __MAX( p_line->i_height,
1701 p_face->size->metrics.height >> 6 );
1702 p_line->pp_glyphs[ i ] = NULL;
1704 p_result->x = __MAX( p_result->x, line.xMax );
1705 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1706 i_yMax - i_yMin ) );
1713 *psz_unicode = '\n';
1715 psz_unicode = psz_unicode_start;
1716 *pi_pen_x = i_pen_x_start;
1724 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1725 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1727 i_previous = i_glyph_index;
1728 *pi_pen_x += p_face->glyph->advance.x >> 6;
1731 p_line->i_width = line.xMax;
1732 p_line->i_height = __MAX( p_line->i_height,
1733 p_face->size->metrics.height >> 6 );
1734 p_line->pp_glyphs[ i ] = NULL;
1736 p_result->x = __MAX( p_result->x, line.xMax );
1737 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1738 line.yMax - line.yMin ) );
1742 /* Get rid of any text processed - if necessary repositioning
1743 * at the start of a new line of text
1747 *psz_unicode_start = '\0';
1749 else if( psz_unicode > psz_unicode_start )
1751 for( i=0; psz_unicode[ i ]; i++ )
1752 psz_unicode_start[ i ] = psz_unicode[ i ];
1753 psz_unicode_start[ i ] = '\0';
1759 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1760 font_stack_t **p_fonts, int i_scale )
1763 char *psz_fontname = NULL;
1764 uint32_t i_font_color = 0xffffff;
1765 int i_font_alpha = 0;
1766 uint32_t i_karaoke_bg_color = 0x00ffffff;
1767 int i_font_size = 24;
1769 /* Default all attributes to the top font in the stack -- in case not
1770 * all attributes are specified in the sub-font
1772 if( VLC_SUCCESS == PeekFont( p_fonts,
1776 &i_karaoke_bg_color ))
1778 psz_fontname = strdup( psz_fontname );
1779 i_font_size = i_font_size * 1000 / i_scale;
1781 i_font_alpha = (i_font_color >> 24) & 0xff;
1782 i_font_color &= 0x00ffffff;
1784 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1786 char *psz_name = xml_ReaderName( p_xml_reader );
1787 char *psz_value = xml_ReaderValue( p_xml_reader );
1789 if( psz_name && psz_value )
1791 if( !strcasecmp( "face", psz_name ) )
1793 free( psz_fontname );
1794 psz_fontname = strdup( psz_value );
1796 else if( !strcasecmp( "size", psz_name ) )
1798 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1800 int i_value = atoi( psz_value );
1802 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1803 i_font_size += ( i_value * i_font_size ) / 10;
1804 else if( i_value < -5 )
1805 i_font_size = - i_value;
1806 else if( i_value > 5 )
1807 i_font_size = i_value;
1810 i_font_size = atoi( psz_value );
1812 else if( !strcasecmp( "color", psz_name ) &&
1813 ( psz_value[0] == '#' ) )
1815 i_font_color = strtol( psz_value + 1, NULL, 16 );
1816 i_font_color &= 0x00ffffff;
1818 else if( !strcasecmp( "alpha", psz_name ) &&
1819 ( psz_value[0] == '#' ) )
1821 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1822 i_font_alpha &= 0xff;
1828 rv = PushFont( p_fonts,
1830 i_font_size * i_scale / 1000,
1831 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1832 i_karaoke_bg_color );
1834 free( psz_fontname );
1839 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1840 uint32_t **psz_text_out, uint32_t *pi_runs,
1841 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1842 ft_style_t *p_style )
1844 uint32_t i_string_length = 0;
1846 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1847 *psz_text_out += i_string_length;
1849 if( ppp_styles && ppi_run_lengths )
1855 *ppp_styles = (ft_style_t **)
1856 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1858 else if( *pi_runs == 1 )
1860 *ppp_styles = (ft_style_t **)
1861 malloc( *pi_runs * sizeof( ft_style_t * ) );
1864 /* We have just malloc'ed this memory successfully -
1865 * *pi_runs HAS to be within the memory area of *ppp_styles */
1868 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1872 if( *ppi_run_lengths )
1874 *ppi_run_lengths = (uint32_t *)
1875 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1877 else if( *pi_runs == 1 )
1879 *ppi_run_lengths = (uint32_t *)
1880 malloc( *pi_runs * sizeof( uint32_t ) );
1883 /* same remarks here */
1884 if( *ppi_run_lengths )
1886 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1889 /* If we couldn't use the p_style argument due to memory allocation
1890 * problems above, release it here.
1892 if( p_style ) DeleteStyle( p_style );
1895 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1896 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1898 /* Karaoke tags _PRECEDE_ the text they specify a duration
1899 * for, therefore we are working out the length for the
1900 * previous tag, and first time through we have nothing
1902 if( pi_k_run_lengths )
1907 /* Work out how many characters are presently in the string
1909 for( i = 0; i < i_runs; i++ )
1910 i_chars += pi_run_lengths[ i ];
1912 /* Subtract away those we've already allocated to other
1915 for( i = 0; i < i_k_runs; i++ )
1916 i_chars -= pi_k_run_lengths[ i ];
1918 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1922 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1923 uint32_t **ppi_k_run_lengths,
1924 uint32_t **ppi_k_durations )
1926 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1928 char *psz_name = xml_ReaderName( p_xml_reader );
1929 char *psz_value = xml_ReaderValue( p_xml_reader );
1931 if( psz_name && psz_value &&
1932 !strcasecmp( "t", psz_name ) )
1934 if( ppi_k_durations && ppi_k_run_lengths )
1938 if( *ppi_k_durations )
1940 *ppi_k_durations = (uint32_t *)
1941 realloc( *ppi_k_durations,
1942 *pi_k_runs * sizeof( uint32_t ) );
1944 else if( *pi_k_runs == 1 )
1946 *ppi_k_durations = (uint32_t *)
1947 malloc( *pi_k_runs * sizeof( uint32_t ) );
1950 if( *ppi_k_run_lengths )
1952 *ppi_k_run_lengths = (uint32_t *)
1953 realloc( *ppi_k_run_lengths,
1954 *pi_k_runs * sizeof( uint32_t ) );
1956 else if( *pi_k_runs == 1 )
1958 *ppi_k_run_lengths = (uint32_t *)
1959 malloc( *pi_k_runs * sizeof( uint32_t ) );
1961 if( *ppi_k_durations )
1962 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1964 if( *ppi_k_run_lengths )
1965 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1973 static int ProcessNodes( filter_t *p_filter,
1974 xml_reader_t *p_xml_reader,
1975 text_style_t *p_font_style,
1980 uint32_t **ppi_run_lengths,
1981 ft_style_t ***ppp_styles,
1984 uint32_t *pi_k_runs,
1985 uint32_t **ppi_k_run_lengths,
1986 uint32_t **ppi_k_durations )
1988 int rv = VLC_SUCCESS;
1989 filter_sys_t *p_sys = p_filter->p_sys;
1990 uint32_t *psz_text_orig = psz_text;
1991 font_stack_t *p_fonts = NULL;
1995 char *psz_node = NULL;
1997 bool b_italic = false;
1998 bool b_bold = false;
1999 bool b_uline = false;
2001 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2002 i_scale = val.i_int;
2006 rv = PushFont( &p_fonts,
2007 p_font_style->psz_fontname,
2008 p_font_style->i_font_size * i_scale / 1000,
2009 (p_font_style->i_font_color & 0xffffff) |
2010 ((p_font_style->i_font_alpha & 0xff) << 24),
2011 (p_font_style->i_karaoke_background_color & 0xffffff) |
2012 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2014 if( p_font_style->i_style_flags & STYLE_BOLD )
2016 if( p_font_style->i_style_flags & STYLE_ITALIC )
2018 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2023 rv = PushFont( &p_fonts,
2029 if( rv != VLC_SUCCESS )
2032 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2034 switch ( xml_ReaderNodeType( p_xml_reader ) )
2036 case XML_READER_NONE:
2038 case XML_READER_ENDELEM:
2039 psz_node = xml_ReaderName( p_xml_reader );
2043 if( !strcasecmp( "font", psz_node ) )
2044 PopFont( &p_fonts );
2045 else if( !strcasecmp( "b", psz_node ) )
2047 else if( !strcasecmp( "i", psz_node ) )
2049 else if( !strcasecmp( "u", psz_node ) )
2055 case XML_READER_STARTELEM:
2056 psz_node = xml_ReaderName( p_xml_reader );
2059 if( !strcasecmp( "font", psz_node ) )
2060 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2061 else if( !strcasecmp( "b", psz_node ) )
2063 else if( !strcasecmp( "i", psz_node ) )
2065 else if( !strcasecmp( "u", psz_node ) )
2067 else if( !strcasecmp( "br", psz_node ) )
2069 SetupLine( p_filter, "\n", &psz_text,
2070 pi_runs, ppi_run_lengths, ppp_styles,
2071 GetStyleFromFontStack( p_sys,
2077 else if( !strcasecmp( "k", psz_node ) )
2079 /* Only valid in karaoke */
2082 if( *pi_k_runs > 0 )
2084 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2085 *pi_k_runs, *ppi_k_run_lengths );
2087 SetupKaraoke( p_xml_reader, pi_k_runs,
2088 ppi_k_run_lengths, ppi_k_durations );
2095 case XML_READER_TEXT:
2096 psz_node = xml_ReaderValue( p_xml_reader );
2099 /* Turn any multiple-whitespaces into single spaces */
2100 char *s = strpbrk( psz_node, "\t\r\n " );
2103 int i_whitespace = strspn( s, "\t\r\n " );
2105 if( i_whitespace > 1 )
2108 strlen( s ) - i_whitespace + 1 );
2111 s = strpbrk( s, "\t\r\n " );
2113 SetupLine( p_filter, psz_node, &psz_text,
2114 pi_runs, ppi_run_lengths, ppp_styles,
2115 GetStyleFromFontStack( p_sys,
2124 if( rv != VLC_SUCCESS )
2126 psz_text = psz_text_orig;
2132 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2133 *pi_k_runs, *ppi_k_run_lengths );
2136 *pi_len = psz_text - psz_text_orig;
2138 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2143 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2147 for( k=0; k < p_sys->i_font_attachments; k++ )
2149 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2151 FT_Face p_face = NULL;
2153 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2161 bool match = !strcasecmp( p_face->family_name,
2162 p_style->psz_fontname );
2164 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2165 match = match && p_style->b_bold;
2167 match = match && !p_style->b_bold;
2169 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2170 match = match && p_style->b_italic;
2172 match = match && !p_style->b_italic;
2180 FT_Done_Face( p_face );
2185 return VLC_EGENERIC;
2188 static int BuildDone( vlc_object_t *p_this, const char *psz_var,
2189 vlc_value_t oldval, vlc_value_t newval, void *param )
2194 ((filter_sys_t*)param)->b_fontconfig_ok = newval.b_bool;
2195 assert( newval.b_bool );
2199 static int ProcessLines( filter_t *p_filter,
2204 uint32_t *pi_run_lengths,
2205 ft_style_t **pp_styles,
2206 line_desc_t **pp_lines,
2208 FT_Vector *p_result,
2212 uint32_t *pi_k_run_lengths,
2213 uint32_t *pi_k_durations )
2215 filter_sys_t *p_sys = p_filter->p_sys;
2216 ft_style_t **pp_char_styles;
2217 int *p_new_positions = NULL;
2218 int8_t *p_levels = NULL;
2219 uint8_t *pi_karaoke_bar = NULL;
2223 /* Assign each character in the text string its style explicitly, so that
2224 * after the characters have been shuffled around by Fribidi, we can re-apply
2225 * the styles, and to simplify the calculation of runs within a line.
2227 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2228 if( !pp_char_styles )
2233 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2234 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2235 * we just won't be able to display the progress bar; at least we'll
2241 for( j = 0; j < i_runs; j++ )
2242 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2243 pp_char_styles[ i++ ] = pp_styles[ j ];
2245 #if defined(HAVE_FRIBIDI)
2247 ft_style_t **pp_char_styles_new;
2248 int *p_old_positions;
2249 uint32_t *p_fribidi_string;
2250 int start_pos, pos = 0;
2252 pp_char_styles_new = (ft_style_t **)
2253 malloc( i_len * sizeof( ft_style_t * ));
2255 p_fribidi_string = (uint32_t *)
2256 malloc( (i_len + 1) * sizeof(uint32_t) );
2257 p_old_positions = (int *)
2258 malloc( (i_len + 1) * sizeof( int ) );
2259 p_new_positions = (int *)
2260 malloc( (i_len + 1) * sizeof( int ) );
2261 p_levels = (int8_t *)
2262 malloc( (i_len + 1) * sizeof( int8_t ) );
2264 if( ! pp_char_styles_new ||
2265 ! p_fribidi_string ||
2266 ! p_old_positions ||
2267 ! p_new_positions ||
2271 free( p_old_positions );
2272 free( p_new_positions );
2273 free( p_fribidi_string );
2274 free( pp_char_styles_new );
2275 free( pi_karaoke_bar );
2277 free( pp_char_styles );
2281 /* Do bidi conversion line-by-line */
2284 while(pos < i_len) {
2285 if (psz_text[pos] != '\n')
2287 p_fribidi_string[pos] = psz_text[pos];
2288 pp_char_styles_new[pos] = pp_char_styles[pos];
2289 p_new_positions[pos] = pos;
2294 while(pos < i_len) {
2295 if (psz_text[pos] == '\n')
2299 if (pos > start_pos)
2301 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2302 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2303 pos - start_pos, &base_dir,
2304 (FriBidiChar*)p_fribidi_string + start_pos,
2305 p_new_positions + start_pos,
2307 p_levels + start_pos );
2308 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2310 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2311 p_old_positions[ j - start_pos ] ];
2312 p_new_positions[ j ] += start_pos;
2316 free( p_old_positions );
2317 free( pp_char_styles );
2318 pp_char_styles = pp_char_styles_new;
2319 psz_text = p_fribidi_string;
2320 p_fribidi_string[ i_len ] = 0;
2323 /* Work out the karaoke */
2324 if( pi_karaoke_bar )
2326 int64_t i_last_duration = 0;
2327 int64_t i_duration = 0;
2328 int64_t i_start_pos = 0;
2329 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2331 for( k = 0; k< i_k_runs; k++ )
2333 double fraction = 0.0;
2335 i_duration += pi_k_durations[ k ];
2337 if( i_duration < i_elapsed )
2339 /* Completely finished this run-length -
2340 * let it render normally */
2344 else if( i_elapsed < i_last_duration )
2346 /* Haven't got up to this segment yet -
2347 * render it completely in karaoke BG mode */
2353 /* Partway through this run */
2355 fraction = (double)(i_elapsed - i_last_duration) /
2356 (double)pi_k_durations[ k ];
2358 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2360 double shade = pi_k_run_lengths[ k ] * fraction;
2362 if( p_new_positions )
2363 j = p_new_positions[ i_start_pos + i ];
2365 j = i_start_pos + i;
2367 if( i < (uint32_t)shade )
2368 pi_karaoke_bar[ j ] = 0xff;
2369 else if( (double)i > shade )
2370 pi_karaoke_bar[ j ] = 0x00;
2373 shade -= (int)shade;
2374 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2375 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2379 i_last_duration = i_duration;
2380 i_start_pos += pi_k_run_lengths[ k ];
2384 free( p_new_positions );
2386 FT_Vector tmp_result;
2388 line_desc_t *p_line = NULL;
2389 line_desc_t *p_prev = NULL;
2395 p_result->x = p_result->y = 0;
2396 tmp_result.x = tmp_result.y = 0;
2399 for( k = 0; k <= (uint32_t) i_len; k++ )
2401 if( ( k == (uint32_t) i_len ) ||
2403 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2405 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2407 /* End of the current style run */
2408 FT_Face p_face = NULL;
2411 /* Look for a match amongst our attachments first */
2412 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2414 if( ! p_face && p_sys->b_fontconfig_ok )
2417 vlc_mutex_lock( &p_sys->fontconfig_lock );
2419 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2420 p_style->psz_fontname,
2424 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2426 if( psz_fontfile && ! *psz_fontfile )
2428 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2429 " so using default font", p_style->psz_fontname,
2430 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2431 (p_style->b_bold ? "(Bold)" :
2432 (p_style->b_italic ? "(Italic)" : ""))) );
2433 free( psz_fontfile );
2434 psz_fontfile = NULL;
2439 if( FT_New_Face( p_sys->p_library,
2440 psz_fontfile, i_idx, &p_face ) )
2442 free( psz_fontfile );
2443 free( pp_char_styles );
2444 #if defined(HAVE_FRIBIDI)
2447 free( pi_karaoke_bar );
2448 return VLC_EGENERIC;
2450 free( psz_fontfile );
2454 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2456 /* We've loaded a font face which is unhelpful for actually
2457 * rendering text - fallback to the default one.
2459 FT_Done_Face( p_face );
2463 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2464 ft_encoding_unicode ) ||
2465 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2466 p_style->i_font_size ) )
2468 if( p_face ) FT_Done_Face( p_face );
2469 free( pp_char_styles );
2470 #if defined(HAVE_FRIBIDI)
2473 free( pi_karaoke_bar );
2474 return VLC_EGENERIC;
2476 p_sys->i_use_kerning =
2477 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2480 uint32_t *psz_unicode = (uint32_t *)
2481 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2484 if( p_face ) FT_Done_Face( p_face );
2485 free( pp_char_styles );
2486 free( psz_unicode );
2487 #if defined(HAVE_FRIBIDI)
2490 free( pi_karaoke_bar );
2493 memcpy( psz_unicode, psz_text + i_prev,
2494 sizeof( uint32_t ) * ( k - i_prev ) );
2495 psz_unicode[ k - i_prev ] = 0;
2496 while( *psz_unicode )
2500 if( !(p_line = NewLine( i_len - i_prev)) )
2502 if( p_face ) FT_Done_Face( p_face );
2503 free( pp_char_styles );
2504 free( psz_unicode );
2505 #if defined(HAVE_FRIBIDI)
2508 free( pi_karaoke_bar );
2511 /* New Color mode only works in YUVA rendering mode --
2512 * (RGB mode has palette constraints on it). We therefore
2513 * need to populate the legacy colour fields also.
2515 p_line->b_new_color_mode = true;
2516 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2517 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2518 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2519 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2520 p_line->p_next = NULL;
2522 i_pen_y += tmp_result.y;
2526 if( p_prev ) p_prev->p_next = p_line;
2527 else *pp_lines = p_line;
2530 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2531 p_style->i_font_color, p_style->b_underline,
2532 p_style->i_karaoke_bg_color,
2533 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2534 &tmp_result ) != VLC_SUCCESS )
2536 if( p_face ) FT_Done_Face( p_face );
2537 free( pp_char_styles );
2538 free( psz_unicode );
2539 #if defined(HAVE_FRIBIDI)
2542 free( pi_karaoke_bar );
2543 return VLC_EGENERIC;
2548 p_result->x = __MAX( p_result->x, tmp_result.x );
2549 p_result->y += tmp_result.y;
2554 if( *psz_unicode == '\n')
2558 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2560 *c_ptr = *(c_ptr+1);
2565 free( psz_unicode );
2566 if( p_face ) FT_Done_Face( p_face );
2570 free( pp_char_styles );
2571 #if defined(HAVE_FRIBIDI)
2576 p_result->x = __MAX( p_result->x, tmp_result.x );
2577 p_result->y += tmp_result.y;
2580 if( pi_karaoke_bar )
2583 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2585 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2587 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2591 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2593 /* 100% BG colour will render faster if we
2594 * instead make it 100% FG colour, so leave
2595 * the ratio alone and copy the value across
2597 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2601 if( pi_karaoke_bar[ i ] & 0x80 )
2603 /* Swap Left and Right sides over for Right aligned
2604 * language text (eg. Arabic, Hebrew)
2606 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2608 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2609 p_line->p_bg_rgb[ k ] = i_tmp;
2611 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2614 /* Jump over the '\n' at the line-end */
2617 free( pi_karaoke_bar );
2623 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2624 subpicture_region_t *p_region_in )
2626 int rv = VLC_SUCCESS;
2627 stream_t *p_sub = NULL;
2628 xml_t *p_xml = NULL;
2629 xml_reader_t *p_xml_reader = NULL;
2631 if( !p_region_in || !p_region_in->psz_html )
2632 return VLC_EGENERIC;
2634 /* Reset the default fontsize in case screen metrics have changed */
2635 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2637 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2638 (uint8_t *) p_region_in->psz_html,
2639 strlen( p_region_in->psz_html ),
2643 p_xml = xml_Create( p_filter );
2646 bool b_karaoke = false;
2648 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2651 /* Look for Root Node */
2652 if( xml_ReaderRead( p_xml_reader ) == 1 )
2654 char *psz_node = xml_ReaderName( p_xml_reader );
2656 if( !strcasecmp( "karaoke", psz_node ) )
2658 /* We're going to have to render the text a number
2659 * of times to show the progress marker on the text.
2661 var_SetBool( p_filter, "text-rerender", true );
2664 else if( !strcasecmp( "text", psz_node ) )
2670 /* Only text and karaoke tags are supported */
2671 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2672 xml_ReaderDelete( p_xml, p_xml_reader );
2673 p_xml_reader = NULL;
2685 uint32_t i_runs = 0;
2686 uint32_t i_k_runs = 0;
2687 uint32_t *pi_run_lengths = NULL;
2688 uint32_t *pi_k_run_lengths = NULL;
2689 uint32_t *pi_k_durations = NULL;
2690 ft_style_t **pp_styles = NULL;
2692 line_desc_t *p_lines = NULL;
2694 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2695 sizeof( uint32_t ) );
2700 rv = ProcessNodes( p_filter, p_xml_reader,
2701 p_region_in->p_style, psz_text, &i_len,
2702 &i_runs, &pi_run_lengths, &pp_styles,
2703 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2706 p_region_out->i_x = p_region_in->i_x;
2707 p_region_out->i_y = p_region_in->i_y;
2709 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2711 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2712 pi_run_lengths, pp_styles, &p_lines, &result,
2713 b_karaoke, i_k_runs, pi_k_run_lengths,
2717 for( k=0; k<i_runs; k++)
2718 DeleteStyle( pp_styles[k] );
2720 free( pi_run_lengths );
2723 /* Don't attempt to render text that couldn't be layed out
2726 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2728 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2730 Render( p_filter, p_region_out, p_lines,
2731 result.x, result.y );
2735 RenderYUVA( p_filter, p_region_out, p_lines,
2736 result.x, result.y );
2740 FreeLines( p_lines );
2742 xml_ReaderDelete( p_xml, p_xml_reader );
2744 xml_Delete( p_xml );
2746 stream_Delete( p_sub );
2752 static char* FontConfig_Select( FcConfig* priv, const char* family,
2753 bool b_bold, bool b_italic, int *i_idx )
2756 FcPattern *pat, *p_pat;
2760 pat = FcPatternCreate();
2761 if (!pat) return NULL;
2763 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2764 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2765 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2766 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2768 FcDefaultSubstitute( pat );
2770 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2772 FcPatternDestroy( pat );
2776 p_pat = FcFontMatch( priv, pat, &result );
2777 FcPatternDestroy( pat );
2778 if( !p_pat ) return NULL;
2780 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2781 || ( val_b != FcTrue ) )
2783 FcPatternDestroy( p_pat );
2786 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2791 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2793 FcPatternDestroy( p_pat );
2798 if( strcasecmp((const char*)val_s, family ) != 0 )
2799 msg_Warn( p_filter, "fontconfig: selected font family is not"
2800 "the requested one: '%s' != '%s'\n",
2801 (const char*)val_s, family );
2804 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2806 FcPatternDestroy( p_pat );
2810 FcPatternDestroy( p_pat );
2811 return strdup( (const char*)val_s );
2815 static void FreeLine( line_desc_t *p_line )
2818 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2820 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2822 free( p_line->pp_glyphs );
2823 free( p_line->p_glyph_pos );
2824 free( p_line->p_fg_rgb );
2825 free( p_line->p_bg_rgb );
2826 free( p_line->p_fg_bg_ratio );
2827 free( p_line->pi_underline_offset );
2828 free( p_line->pi_underline_thickness );
2832 static void FreeLines( line_desc_t *p_lines )
2834 line_desc_t *p_line, *p_next;
2836 if( !p_lines ) return;
2838 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2840 p_next = p_line->p_next;
2845 static line_desc_t *NewLine( int i_count )
2847 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2849 if( !p_line ) return NULL;
2850 p_line->i_height = 0;
2851 p_line->i_width = 0;
2852 p_line->p_next = NULL;
2854 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2855 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2856 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2857 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2858 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2859 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2860 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2861 if( ( p_line->pp_glyphs == NULL ) ||
2862 ( p_line->p_glyph_pos == NULL ) ||
2863 ( p_line->p_fg_rgb == NULL ) ||
2864 ( p_line->p_bg_rgb == NULL ) ||
2865 ( p_line->p_fg_bg_ratio == NULL ) ||
2866 ( p_line->pi_underline_offset == NULL ) ||
2867 ( p_line->pi_underline_thickness == NULL ) )
2869 free( p_line->pi_underline_thickness );
2870 free( p_line->pi_underline_offset );
2871 free( p_line->p_fg_rgb );
2872 free( p_line->p_bg_rgb );
2873 free( p_line->p_fg_bg_ratio );
2874 free( p_line->p_glyph_pos );
2875 free( p_line->pp_glyphs );
2879 p_line->pp_glyphs[0] = NULL;
2880 p_line->b_new_color_mode = false;
2885 static int GetFontSize( filter_t *p_filter )
2887 filter_sys_t *p_sys = p_filter->p_sys;
2891 if( p_sys->i_default_font_size )
2893 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2894 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2896 i_size = p_sys->i_default_font_size;
2900 var_Get( p_filter, "freetype-rel-fontsize", &val );
2901 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2902 p_filter->p_sys->i_display_height =
2903 p_filter->fmt_out.video.i_height;
2907 msg_Warn( p_filter, "invalid fontsize, using 12" );
2908 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2909 i_size = 12 * val.i_int / 1000;
2916 static int SetFontSize( filter_t *p_filter, int i_size )
2918 filter_sys_t *p_sys = p_filter->p_sys;
2922 i_size = GetFontSize( p_filter );
2924 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2927 p_sys->i_font_size = i_size;
2929 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2931 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2932 return VLC_EGENERIC;
2938 static void YUVFromRGB( uint32_t i_argb,
2939 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2941 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2942 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2943 int i_blue = ( i_argb & 0x000000ff );
2945 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2946 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2947 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2948 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2949 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2950 -585 * i_blue + 4096 + 1048576) >> 13, 240);