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 *****************************************************************************/
37 #include <vlc_block.h>
38 #include <vlc_filter.h>
39 #include <vlc_stream.h>
41 #include <vlc_input.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>
75 typedef struct line_desc_t line_desc_t;
77 /*****************************************************************************
79 *****************************************************************************/
80 static int Create ( vlc_object_t * );
81 static void Destroy( vlc_object_t * );
83 static int LoadFontsFromAttachments( filter_t *p_filter );
85 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
86 static int RenderText( filter_t *, subpicture_region_t *,
87 subpicture_region_t * );
88 #ifdef HAVE_FONTCONFIG
89 static int RenderHtml( filter_t *, subpicture_region_t *,
90 subpicture_region_t * );
91 static char *FontConfig_Select( FcConfig *, const char *,
93 static int BuildDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
96 static line_desc_t *NewLine( int );
98 static int GetFontSize( filter_t *p_filter );
99 static int SetFontSize( filter_t *, int );
100 static void YUVFromRGB( uint32_t i_argb,
101 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
103 /*****************************************************************************
105 *****************************************************************************/
106 #define FONT_TEXT N_("Font")
107 #define FONT_LONGTEXT N_("Filename for the font you want to use")
108 #define FONTSIZE_TEXT N_("Font size in pixels")
109 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
110 "that will be rendered on the video. " \
111 "If set to something different than 0 this option will override the " \
112 "relative font size." )
113 #define OPACITY_TEXT N_("Opacity")
114 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
115 "text that will be rendered on the video. 0 = transparent, " \
116 "255 = totally opaque. " )
117 #define COLOR_TEXT N_("Text default color")
118 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
119 "the video. This must be an hexadecimal (like HTML colors). The first two "\
120 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
121 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
122 #define FONTSIZER_TEXT N_("Relative font size")
123 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
124 "fonts that will be rendered on the video. If absolute font size is set, "\
125 "relative size will be overriden." )
127 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
128 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
129 N_("Large"), N_("Larger") };
130 #define YUVP_TEXT N_("Use YUVP renderer")
131 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
132 "This option is only needed if you want to encode into DVB subtitles" )
133 #define EFFECT_TEXT N_("Font Effect")
134 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
135 "text to improve its readability." )
137 #define EFFECT_BACKGROUND 1
138 #define EFFECT_OUTLINE 2
139 #define EFFECT_OUTLINE_FAT 3
141 static int pi_effects[] = { 1, 2, 3 };
142 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
144 static int pi_color_values[] = {
145 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
146 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
147 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
149 static const char *ppsz_color_descriptions[] = {
150 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
151 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
152 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
155 set_shortname( _("Text renderer"));
156 set_description( _("Freetype2 font renderer") );
157 set_category( CAT_VIDEO );
158 set_subcategory( SUBCAT_VIDEO_SUBPIC );
160 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
163 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
164 FONTSIZE_LONGTEXT, true );
166 /* opacity valid on 0..255, with default 255 = fully opaque */
167 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
168 OPACITY_TEXT, OPACITY_LONGTEXT, true );
170 /* hook to the color values list, with default 0x00ffffff = white */
171 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
172 COLOR_LONGTEXT, false );
173 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
175 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
176 FONTSIZER_LONGTEXT, false );
177 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
178 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
179 EFFECT_LONGTEXT, false );
180 change_integer_list( pi_effects, ppsz_effects_text, 0 );
182 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
183 YUVP_LONGTEXT, true );
184 set_capability( "text renderer", 100 );
185 add_shortcut( "text" );
186 set_callbacks( Create, Destroy );
191 /** NULL-terminated list of glyphs making the string */
192 FT_BitmapGlyph *pp_glyphs;
193 /** list of relative positions for the glyphs */
194 FT_Vector *p_glyph_pos;
195 /** list of RGB information for styled text
196 * -- if the rendering mode supports it (RenderYUVA) and
197 * b_new_color_mode is set, then it becomes possible to
198 * have multicoloured text within the subtitles. */
201 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
202 bool b_new_color_mode;
203 /** underline information -- only supplied if text should be underlined */
204 uint16_t *pi_underline_offset;
205 uint16_t *pi_underline_thickness;
209 int i_red, i_green, i_blue;
215 typedef struct font_stack_t font_stack_t;
220 uint32_t i_color; /* ARGB */
221 uint32_t i_karaoke_bg_color; /* ARGB */
223 font_stack_t *p_next;
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 * );
240 #ifdef HAVE_FONTCONFIG
241 static void FontBuilder( vlc_object_t *p_this);
244 /*****************************************************************************
245 * filter_sys_t: freetype local data
246 *****************************************************************************
247 * This structure is part of the video output thread descriptor.
248 * It describes the freetype specific properties of an output thread.
249 *****************************************************************************/
252 FT_Library p_library; /* handle to library */
253 FT_Face p_face; /* handle to face object */
255 uint8_t i_font_opacity;
260 int i_default_font_size;
261 int i_display_height;
262 #ifdef HAVE_FONTCONFIG
263 FcConfig *p_fontconfig;
264 bool b_fontconfig_ok;
265 vlc_mutex_t fontconfig_lock;
268 input_attachment_t **pp_font_attachments;
269 int i_font_attachments;
272 /*****************************************************************************
273 * Create: allocates osd-text video thread output method
274 *****************************************************************************
275 * This function allocates and initializes a Clone vout method.
276 *****************************************************************************/
277 static int Create( vlc_object_t *p_this )
279 filter_t *p_filter = (filter_t *)p_this;
281 char *psz_fontfile = NULL;
284 vlc_object_t *p_fontbuilder;
286 /* Allocate structure */
287 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
290 msg_Err( p_filter, "out of memory" );
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 );
325 msg_Err( p_filter, "out of memory" );
329 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
330 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
331 #elif defined(__APPLE__)
332 strcpy( psz_fontfile, DEFAULT_FONT );
334 msg_Err( p_filter, "user didn't specify a font" );
339 i_error = FT_Init_FreeType( &p_sys->p_library );
342 msg_Err( p_filter, "couldn't initialize freetype" );
345 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
347 if( i_error == FT_Err_Unknown_File_Format )
349 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
354 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
358 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
361 msg_Err( p_filter, "font has no unicode translation table" );
365 #ifdef HAVE_FONTCONFIG
366 vlc_mutex_init( &p_sys->fontconfig_lock );
367 p_sys->b_fontconfig_ok = false;
368 p_sys->p_fontconfig = NULL;
370 /* Check for an existing Fontbuilder thread */
371 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
372 p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
376 if( ! p_fontbuilder )
378 /* Create the FontBuilder thread as a child of a top-level
379 * object, so that it can survive the destruction of the
380 * freetype object - the fontlist only needs to be built once,
381 * and calling the fontbuild a second time while the first is
382 * still in progress can cause thread instabilities.
385 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
386 VLC_OBJECT_GENERIC );
389 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
390 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
392 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
393 var_SetBool( p_fontbuilder, "build-done", false );
394 var_AddCallback( p_fontbuilder, "build-done", BuildDone, p_sys );
396 if( vlc_thread_create( p_fontbuilder,
399 VLC_THREAD_PRIORITY_LOW,
402 /* Don't destroy the fontconfig object - we won't be able to do
403 * italics or bold or change the font face, but we will still
404 * be able to do underline and change the font size.
406 msg_Warn( p_filter, "fontconfig database builder thread can't "
407 "be launched. Font styling support will be limited." );
412 vlc_object_release( p_fontbuilder );
417 vlc_object_release( p_fontbuilder );
419 vlc_mutex_unlock( lock );
423 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
425 var_Get( p_filter, "freetype-fontsize", &val );
426 p_sys->i_default_font_size = val.i_int;
427 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
429 free( psz_fontfile );
431 p_sys->pp_font_attachments = NULL;
432 p_sys->i_font_attachments = 0;
434 p_filter->pf_render_text = RenderText;
435 #ifdef HAVE_FONTCONFIG
436 p_filter->pf_render_html = RenderHtml;
438 p_filter->pf_render_html = NULL;
441 LoadFontsFromAttachments( p_filter );
446 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
447 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
448 free( psz_fontfile );
453 /*****************************************************************************
454 * Destroy: destroy Clone video thread output method
455 *****************************************************************************
456 * Clean up all data and library connections
457 *****************************************************************************/
458 static void Destroy( vlc_object_t *p_this )
460 filter_t *p_filter = (filter_t *)p_this;
461 filter_sys_t *p_sys = p_filter->p_sys;
463 if( p_sys->pp_font_attachments )
467 for( k = 0; k < p_sys->i_font_attachments; k++ )
469 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
472 free( p_sys->pp_font_attachments );
475 #ifdef HAVE_FONTCONFIG
476 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
477 vlc_object_t *p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
478 "fontlist builder", FIND_CHILD );
481 var_DelCallback( p_fontbuilder, "build-done", BuildDone, p_sys );
482 vlc_object_release( p_fontbuilder );
484 vlc_mutex_unlock( lock );
486 vlc_mutex_destroy( &p_sys->fontconfig_lock );
488 if( p_sys->p_fontconfig )
490 FcConfigDestroy( p_sys->p_fontconfig );
491 p_sys->p_fontconfig = NULL;
493 /* FcFini asserts calling the subfunction FcCacheFini()
494 * even if no other library functions have been made since FcInit(),
495 * so don't call it. */
497 FT_Done_Face( p_sys->p_face );
498 FT_Done_FreeType( p_sys->p_library );
502 #ifdef HAVE_FONTCONFIG
504 static void FontBuilder( vlc_object_t *p_this )
506 FcConfig *p_fontconfig = FcInitLoadConfig();
509 vlc_thread_ready( p_this );
515 msg_Dbg( p_this, "Building font database..." );
517 if(! FcConfigBuildFonts( p_fontconfig ))
519 /* Don't destroy the fontconfig object - we won't be able to do
520 * italics or bold or change the font face, but we will still
521 * be able to do underline and change the font size.
523 msg_Err( p_this, "fontconfig database can't be built. "
524 "Font styling won't be available" );
528 msg_Dbg( p_this, "Finished building font database." );
529 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
531 lock = var_AcquireMutex( "fontbuilder" );
533 var_SetBool( p_this, "build-done", true );
535 FcConfigDestroy( p_fontconfig );
536 vlc_mutex_unlock( lock );
538 vlc_object_detach( p_this );
539 vlc_object_release( p_this );
544 /*****************************************************************************
545 * Make any TTF/OTF fonts present in the attachments of the media file
546 * and store them for later use by the FreeType Engine
547 *****************************************************************************/
548 static int LoadFontsFromAttachments( filter_t *p_filter )
550 filter_sys_t *p_sys = p_filter->p_sys;
551 input_thread_t *p_input;
552 input_attachment_t **pp_attachments;
553 int i_attachments_cnt;
555 int rv = VLC_SUCCESS;
557 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
561 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
563 vlc_object_release(p_input);
567 p_sys->i_font_attachments = 0;
568 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
569 if(! p_sys->pp_font_attachments )
572 for( k = 0; k < i_attachments_cnt; k++ )
574 input_attachment_t *p_attach = pp_attachments[k];
576 if( p_sys->pp_font_attachments )
578 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
579 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
580 ( p_attach->i_data > 0 ) &&
581 ( p_attach->p_data != NULL ) )
583 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
587 vlc_input_attachment_Delete( p_attach );
592 vlc_input_attachment_Delete( p_attach );
595 free( pp_attachments );
597 vlc_object_release(p_input);
602 /*****************************************************************************
603 * Render: place string in picture
604 *****************************************************************************
605 * This function merges the previously rendered freetype glyphs into a picture
606 *****************************************************************************/
607 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
608 line_desc_t *p_line, int i_width, int i_height )
610 static uint8_t pi_gamma[16] =
611 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
612 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
616 int i, x, y, i_pitch;
617 uint8_t i_y; /* YUV values, derived from incoming RGB */
619 subpicture_region_t *p_region_tmp;
621 /* Create a new subpicture region */
622 memset( &fmt, 0, sizeof(video_format_t) );
623 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
625 fmt.i_width = fmt.i_visible_width = i_width + 4;
626 fmt.i_height = fmt.i_visible_height = i_height + 4;
627 if( p_region->fmt.i_visible_width > 0 )
628 fmt.i_visible_width = p_region->fmt.i_visible_width;
629 if( p_region->fmt.i_visible_height > 0 )
630 fmt.i_visible_height = p_region->fmt.i_visible_height;
631 fmt.i_x_offset = fmt.i_y_offset = 0;
632 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
635 msg_Err( p_filter, "cannot allocate SPU region" );
639 p_region->fmt = p_region_tmp->fmt;
640 p_region->picture = p_region_tmp->picture;
641 free( p_region_tmp );
643 /* Calculate text color components */
644 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
645 25 * p_line->i_blue + 128) >> 8) + 16;
646 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
647 112 * p_line->i_blue + 128) >> 8) + 128;
648 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
649 18 * p_line->i_blue + 128) >> 8) + 128;
652 fmt.p_palette->i_entries = 16;
653 for( i = 0; i < 8; i++ )
655 fmt.p_palette->palette[i][0] = 0;
656 fmt.p_palette->palette[i][1] = 0x80;
657 fmt.p_palette->palette[i][2] = 0x80;
658 fmt.p_palette->palette[i][3] = pi_gamma[i];
659 fmt.p_palette->palette[i][3] =
660 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
662 for( i = 8; i < fmt.p_palette->i_entries; i++ )
664 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
665 fmt.p_palette->palette[i][1] = i_u;
666 fmt.p_palette->palette[i][2] = i_v;
667 fmt.p_palette->palette[i][3] = pi_gamma[i];
668 fmt.p_palette->palette[i][3] =
669 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
672 p_dst = p_region->picture.Y_PIXELS;
673 i_pitch = p_region->picture.Y_PITCH;
675 /* Initialize the region pixels */
676 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
678 for( ; p_line != NULL; p_line = p_line->p_next )
680 int i_glyph_tmax = 0;
681 int i_bitmap_offset, i_offset, i_align_offset = 0;
682 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
684 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
685 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
688 if( p_line->i_width < i_width )
690 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
692 i_align_offset = i_width - p_line->i_width;
694 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
696 i_align_offset = ( i_width - p_line->i_width ) / 2;
700 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
702 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
704 i_offset = ( p_line->p_glyph_pos[ i ].y +
705 i_glyph_tmax - p_glyph->top + 2 ) *
706 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
709 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
711 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
713 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
715 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
722 /* Outlining (find something better than nearest neighbour filtering ?) */
725 uint8_t *p_dst = p_region->picture.Y_PIXELS;
726 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
727 uint8_t left, current;
729 for( y = 1; y < (int)fmt.i_height - 1; y++ )
731 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
732 p_dst += p_region->picture.Y_PITCH;
735 for( x = 1; x < (int)fmt.i_width - 1; x++ )
738 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
739 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;
743 memset( p_top, 0, fmt.i_width );
749 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
750 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
751 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
752 int i_glyph_tmax, int i_align_offset,
753 uint8_t i_y, uint8_t i_u, uint8_t i_v,
754 subpicture_region_t *p_region)
758 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
760 p_dst_y = p_region->picture.Y_PIXELS;
761 p_dst_u = p_region->picture.U_PIXELS;
762 p_dst_v = p_region->picture.V_PIXELS;
763 p_dst_a = p_region->picture.A_PIXELS;
764 i_pitch = p_region->picture.A_PITCH;
766 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
767 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
769 for( y = 0; y < i_line_thickness; y++ )
771 int i_extra = p_this_glyph->bitmap.width;
775 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
776 (p_this_glyph_pos->x + p_this_glyph->left);
778 for( x = 0; x < i_extra; x++ )
782 /* break the underline around the tails of any glyphs which cross it */
783 for( z = x - i_line_thickness;
784 z < x + i_line_thickness && b_ok;
787 if( p_next_glyph && ( z >= i_extra ) )
789 int i_row = i_line_offset + p_next_glyph->top + y;
791 if( ( p_next_glyph->bitmap.rows > i_row ) &&
792 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
797 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
799 int i_row = i_line_offset + p_this_glyph->top + y;
801 if( ( p_this_glyph->bitmap.rows > i_row ) &&
802 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
811 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
812 p_dst_u[i_offset+x] = i_u;
813 p_dst_v[i_offset+x] = i_v;
814 p_dst_a[i_offset+x] = 255;
821 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
823 uint8_t *p_dst = p_region->picture.A_PIXELS;
824 int i_pitch = p_region->picture.A_PITCH;
827 for( ; p_line != NULL; p_line = p_line->p_next )
829 int i_glyph_tmax=0, i = 0;
830 int i_bitmap_offset, i_offset, i_align_offset = 0;
831 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
833 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
834 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
837 if( p_line->i_width < i_width )
839 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
841 i_align_offset = i_width - p_line->i_width;
843 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
845 i_align_offset = ( i_width - p_line->i_width ) / 2;
849 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
851 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
853 i_offset = ( p_line->p_glyph_pos[ i ].y +
854 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
855 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
856 i_align_offset +xoffset;
858 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
860 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
862 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
863 if( p_dst[i_offset+x] <
864 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
866 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
875 /*****************************************************************************
876 * Render: place string in picture
877 *****************************************************************************
878 * This function merges the previously rendered freetype glyphs into a picture
879 *****************************************************************************/
880 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
881 line_desc_t *p_line, int i_width, int i_height )
883 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
885 int i, x, y, i_pitch, i_alpha;
886 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
887 subpicture_region_t *p_region_tmp;
889 if( i_width == 0 || i_height == 0 )
892 /* Create a new subpicture region */
893 memset( &fmt, 0, sizeof(video_format_t) );
894 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
896 fmt.i_width = fmt.i_visible_width = i_width + 6;
897 fmt.i_height = fmt.i_visible_height = i_height + 6;
898 if( p_region->fmt.i_visible_width > 0 )
899 fmt.i_visible_width = p_region->fmt.i_visible_width;
900 if( p_region->fmt.i_visible_height > 0 )
901 fmt.i_visible_height = p_region->fmt.i_visible_height;
902 fmt.i_x_offset = fmt.i_y_offset = 0;
903 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
906 msg_Err( p_filter, "cannot allocate SPU region" );
910 p_region->fmt = p_region_tmp->fmt;
911 p_region->picture = p_region_tmp->picture;
912 free( p_region_tmp );
914 /* Calculate text color components */
915 YUVFromRGB( (p_line->i_red << 16) |
916 (p_line->i_green << 8) |
919 i_alpha = p_line->i_alpha;
921 p_dst_y = p_region->picture.Y_PIXELS;
922 p_dst_u = p_region->picture.U_PIXELS;
923 p_dst_v = p_region->picture.V_PIXELS;
924 p_dst_a = p_region->picture.A_PIXELS;
925 i_pitch = p_region->picture.A_PITCH;
927 /* Initialize the region pixels */
928 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
930 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
931 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
932 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
933 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
937 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
938 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
939 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
940 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
942 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
943 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
945 DrawBlack( p_line, i_width, p_region, 0, 0);
946 DrawBlack( p_line, i_width, p_region, -1, 0);
947 DrawBlack( p_line, i_width, p_region, 0, -1);
948 DrawBlack( p_line, i_width, p_region, 1, 0);
949 DrawBlack( p_line, i_width, p_region, 0, 1);
952 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
954 DrawBlack( p_line, i_width, p_region, -1, -1);
955 DrawBlack( p_line, i_width, p_region, -1, 1);
956 DrawBlack( p_line, i_width, p_region, 1, -1);
957 DrawBlack( p_line, i_width, p_region, 1, 1);
959 DrawBlack( p_line, i_width, p_region, -2, 0);
960 DrawBlack( p_line, i_width, p_region, 0, -2);
961 DrawBlack( p_line, i_width, p_region, 2, 0);
962 DrawBlack( p_line, i_width, p_region, 0, 2);
964 DrawBlack( p_line, i_width, p_region, -2, -2);
965 DrawBlack( p_line, i_width, p_region, -2, 2);
966 DrawBlack( p_line, i_width, p_region, 2, -2);
967 DrawBlack( p_line, i_width, p_region, 2, 2);
969 DrawBlack( p_line, i_width, p_region, -3, 0);
970 DrawBlack( p_line, i_width, p_region, 0, -3);
971 DrawBlack( p_line, i_width, p_region, 3, 0);
972 DrawBlack( p_line, i_width, p_region, 0, 3);
975 for( ; p_line != NULL; p_line = p_line->p_next )
977 int i_glyph_tmax = 0;
978 int i_bitmap_offset, i_offset, i_align_offset = 0;
979 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
981 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
982 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
985 if( p_line->i_width < i_width )
987 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
989 i_align_offset = i_width - p_line->i_width;
991 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
993 i_align_offset = ( i_width - p_line->i_width ) / 2;
997 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
999 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1001 i_offset = ( p_line->p_glyph_pos[ i ].y +
1002 i_glyph_tmax - p_glyph->top + 3 ) *
1003 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1006 if( p_line->b_new_color_mode )
1008 /* Every glyph can (and in fact must) have its own color */
1009 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1012 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1014 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1016 uint8_t i_y_local = i_y;
1017 uint8_t i_u_local = i_u;
1018 uint8_t i_v_local = i_v;
1020 if( p_line->p_fg_bg_ratio != 0x00 )
1022 int i_split = p_glyph->bitmap.width *
1023 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1027 YUVFromRGB( p_line->p_bg_rgb[ i ],
1028 &i_y_local, &i_u_local, &i_v_local );
1032 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1034 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1035 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1037 p_dst_u[i_offset+x] = i_u;
1038 p_dst_v[i_offset+x] = i_v;
1040 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1041 p_dst_a[i_offset+x] = 0xff;
1044 i_offset += i_pitch;
1047 if( p_line->pi_underline_thickness[ i ] )
1049 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1050 p_line->pi_underline_offset[ i ],
1051 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1052 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1053 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1054 i_glyph_tmax, i_align_offset,
1061 /* Apply the alpha setting */
1062 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1063 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1069 * This function renders a text subpicture region into another one.
1070 * It also calculates the size needed for this string, and renders the
1071 * needed glyphs into memory. It is used as pf_add_string callback in
1072 * the vout method by this module
1074 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1075 subpicture_region_t *p_region_in )
1077 filter_sys_t *p_sys = p_filter->p_sys;
1078 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1079 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1080 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1081 int i_string_length;
1083 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1084 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1094 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1095 psz_string = p_region_in->psz_text;
1096 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1098 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1099 i_scale = val.i_int;
1101 if( p_region_in->p_style )
1103 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1104 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1105 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1109 i_font_color = p_sys->i_font_color;
1110 i_font_alpha = 255 - p_sys->i_font_opacity;
1111 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1114 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1115 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1116 SetFontSize( p_filter, i_font_size );
1118 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1119 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1120 i_blue = i_font_color & 0x000000FF;
1122 result.x = result.y = 0;
1123 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1125 psz_unicode = psz_unicode_orig =
1126 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1127 if( psz_unicode == NULL )
1129 msg_Err( p_filter, "out of memory" );
1132 #if defined(WORDS_BIGENDIAN)
1133 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1135 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1137 if( iconv_handle == (vlc_iconv_t)-1 )
1139 msg_Warn( p_filter, "unable to do conversion" );
1145 const char *p_in_buffer = psz_string;
1146 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1147 i_in_bytes = strlen( psz_string );
1148 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1149 i_out_bytes_left = i_out_bytes;
1150 p_out_buffer = (char *)psz_unicode;
1151 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1153 &p_out_buffer, &i_out_bytes_left );
1155 vlc_iconv_close( iconv_handle );
1159 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1160 "bytes left %u", (unsigned)i_in_bytes );
1163 *(uint32_t*)p_out_buffer = 0;
1164 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1167 #if defined(HAVE_FRIBIDI)
1169 uint32_t *p_fribidi_string;
1170 int32_t start_pos, pos = 0;
1172 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1173 if( !p_fribidi_string )
1175 msg_Err( p_filter, "out of memory" );
1179 /* Do bidi conversion line-by-line */
1180 while( pos < i_string_length )
1182 while( pos < i_string_length )
1184 i_char = psz_unicode[pos];
1185 if (i_char != '\r' && i_char != '\n')
1187 p_fribidi_string[pos] = i_char;
1191 while( pos < i_string_length )
1193 i_char = psz_unicode[pos];
1194 if (i_char == '\r' || i_char == '\n')
1198 if (pos > start_pos)
1200 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1201 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1204 (FriBidiChar*)p_fribidi_string + start_pos,
1209 free( psz_unicode_orig );
1210 psz_unicode = psz_unicode_orig = p_fribidi_string;
1211 p_fribidi_string[ i_string_length ] = 0;
1215 /* Calculate relative glyph positions and a bounding box for the
1217 if( !(p_line = NewLine( strlen( psz_string ))) )
1219 msg_Err( p_filter, "out of memory" );
1223 i_pen_x = i_pen_y = 0;
1225 psz_line_start = psz_unicode;
1227 #define face p_sys->p_face
1228 #define glyph face->glyph
1230 while( *psz_unicode )
1232 i_char = *psz_unicode++;
1233 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1238 if( i_char == '\n' )
1240 psz_line_start = psz_unicode;
1241 if( !(p_next = NewLine( strlen( psz_string ))) )
1243 msg_Err( p_filter, "out of memory" );
1246 p_line->p_next = p_next;
1247 p_line->i_width = line.xMax;
1248 p_line->i_height = face->size->metrics.height >> 6;
1249 p_line->pp_glyphs[ i ] = NULL;
1250 p_line->i_alpha = i_font_alpha;
1251 p_line->i_red = i_red;
1252 p_line->i_green = i_green;
1253 p_line->i_blue = i_blue;
1256 result.x = __MAX( result.x, line.xMax );
1257 result.y += face->size->metrics.height >> 6;
1260 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1261 i_pen_y += face->size->metrics.height >> 6;
1263 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1268 i_glyph_index = FT_Get_Char_Index( face, i_char );
1269 if( p_sys->i_use_kerning && i_glyph_index
1273 FT_Get_Kerning( face, i_previous, i_glyph_index,
1274 ft_kerning_default, &delta );
1275 i_pen_x += delta.x >> 6;
1278 p_line->p_glyph_pos[ i ].x = i_pen_x;
1279 p_line->p_glyph_pos[ i ].y = i_pen_y;
1280 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1283 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1287 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1290 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1294 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1295 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1298 FT_Done_Glyph( tmp_glyph );
1301 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1304 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1305 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1306 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1308 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1309 p_line->pp_glyphs[ i ] = NULL;
1311 p_line = NewLine( strlen( psz_string ));
1312 if( p_prev ) p_prev->p_next = p_line;
1313 else p_lines = p_line;
1315 uint32_t *psz_unicode_saved = psz_unicode;
1316 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1320 if( psz_unicode == psz_line_start )
1321 { /* try harder to break that line */
1322 psz_unicode = psz_unicode_saved;
1323 while( psz_unicode > psz_line_start &&
1324 *psz_unicode != '_' && *psz_unicode != '/' &&
1325 *psz_unicode != '\\' && *psz_unicode != '.' )
1330 if( psz_unicode == psz_line_start )
1332 msg_Warn( p_filter, "unbreakable string" );
1337 *psz_unicode = '\n';
1339 psz_unicode = psz_line_start;
1342 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1345 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1346 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1348 i_previous = i_glyph_index;
1349 i_pen_x += glyph->advance.x >> 6;
1353 p_line->i_width = line.xMax;
1354 p_line->i_height = face->size->metrics.height >> 6;
1355 p_line->pp_glyphs[ i ] = NULL;
1356 p_line->i_alpha = i_font_alpha;
1357 p_line->i_red = i_red;
1358 p_line->i_green = i_green;
1359 p_line->i_blue = i_blue;
1360 result.x = __MAX( result.x, line.xMax );
1361 result.y += line.yMax - line.yMin;
1366 p_region_out->i_x = p_region_in->i_x;
1367 p_region_out->i_y = p_region_in->i_y;
1369 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1370 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1372 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1374 free( psz_unicode_orig );
1375 FreeLines( p_lines );
1379 free( psz_unicode_orig );
1380 FreeLines( p_lines );
1381 return VLC_EGENERIC;
1384 #ifdef HAVE_FONTCONFIG
1385 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1386 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1387 bool b_italic, bool b_uline )
1389 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1393 p_style->i_font_size = i_font_size;
1394 p_style->i_font_color = i_font_color;
1395 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1396 p_style->b_italic = b_italic;
1397 p_style->b_bold = b_bold;
1398 p_style->b_underline = b_uline;
1400 p_style->psz_fontname = strdup( psz_fontname );
1405 static void DeleteStyle( ft_style_t *p_style )
1409 free( p_style->psz_fontname );
1414 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1421 if(( s1->i_font_size == s2->i_font_size ) &&
1422 ( s1->i_font_color == s2->i_font_color ) &&
1423 ( s1->b_italic == s2->b_italic ) &&
1424 ( s1->b_bold == s2->b_bold ) &&
1425 ( s1->b_underline == s2->b_underline ) &&
1426 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1433 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1434 uint32_t i_color, uint32_t i_karaoke_bg_color )
1436 font_stack_t *p_new;
1439 return VLC_EGENERIC;
1441 p_new = malloc( sizeof( font_stack_t ) );
1445 p_new->p_next = NULL;
1448 p_new->psz_name = strdup( psz_name );
1450 p_new->psz_name = NULL;
1452 p_new->i_size = i_size;
1453 p_new->i_color = i_color;
1454 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1462 font_stack_t *p_last;
1464 for( p_last = *p_font;
1466 p_last = p_last->p_next )
1469 p_last->p_next = p_new;
1474 static int PopFont( font_stack_t **p_font )
1476 font_stack_t *p_last, *p_next_to_last;
1478 if( !p_font || !*p_font )
1479 return VLC_EGENERIC;
1481 p_next_to_last = NULL;
1482 for( p_last = *p_font;
1484 p_last = p_last->p_next )
1486 p_next_to_last = p_last;
1489 if( p_next_to_last )
1490 p_next_to_last->p_next = NULL;
1494 free( p_last->psz_name );
1500 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1501 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1503 font_stack_t *p_last;
1505 if( !p_font || !*p_font )
1506 return VLC_EGENERIC;
1508 for( p_last=*p_font;
1510 p_last=p_last->p_next )
1513 *psz_name = p_last->psz_name;
1514 *i_size = p_last->i_size;
1515 *i_color = p_last->i_color;
1516 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1521 static void IconvText( filter_t *p_filter, const char *psz_string,
1522 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1524 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1526 /* If memory hasn't been allocated for our output string, allocate it here
1527 * - the calling function must now be responsible for freeing it.
1529 if( !*ppsz_unicode )
1530 *ppsz_unicode = (uint32_t *)
1531 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1533 /* We don't need to handle a NULL pointer in *ppsz_unicode
1534 * if we are instead testing for a non NULL value like we are here */
1538 #if defined(WORDS_BIGENDIAN)
1539 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1541 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1543 if( iconv_handle != (vlc_iconv_t)-1 )
1545 char *p_in_buffer, *p_out_buffer;
1546 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1547 i_in_bytes = strlen( psz_string );
1548 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1549 i_out_bytes_left = i_out_bytes;
1550 p_in_buffer = (char *) psz_string;
1551 p_out_buffer = (char *) *ppsz_unicode;
1552 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1553 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1555 vlc_iconv_close( iconv_handle );
1559 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1560 "bytes left %u", (unsigned)i_in_bytes );
1564 *(uint32_t*)p_out_buffer = 0;
1566 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1571 msg_Warn( p_filter, "unable to do conversion" );
1576 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1577 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1580 ft_style_t *p_style = NULL;
1582 char *psz_fontname = NULL;
1583 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1584 uint32_t i_karaoke_bg_color = i_font_color;
1585 int i_font_size = p_sys->i_font_size;
1587 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1588 &i_font_color, &i_karaoke_bg_color ))
1590 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1591 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1596 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1597 bool b_uline, int i_karaoke_bgcolor,
1598 line_desc_t *p_line, uint32_t *psz_unicode,
1599 int *pi_pen_x, int i_pen_y, int *pi_start,
1600 FT_Vector *p_result )
1605 bool b_first_on_line = true;
1608 int i_pen_x_start = *pi_pen_x;
1610 uint32_t *psz_unicode_start = psz_unicode;
1612 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1614 /* Account for part of line already in position */
1615 for( i=0; i<*pi_start; i++ )
1619 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1620 ft_glyph_bbox_pixels, &glyph_size );
1622 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1623 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1624 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1625 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1631 b_first_on_line = false;
1633 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1639 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1640 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1644 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1645 ft_kerning_default, &delta );
1646 *pi_pen_x += delta.x >> 6;
1648 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1649 p_line->p_glyph_pos[ i ].y = i_pen_y;
1651 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1655 "unable to render text FT_Load_Glyph returned %d", i_error );
1656 p_line->pp_glyphs[ i ] = NULL;
1657 return VLC_EGENERIC;
1659 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1663 "unable to render text FT_Get_Glyph returned %d", i_error );
1664 p_line->pp_glyphs[ i ] = NULL;
1665 return VLC_EGENERIC;
1667 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1668 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1671 FT_Done_Glyph( tmp_glyph );
1676 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1677 p_face->size->metrics.y_scale));
1678 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1679 p_face->size->metrics.y_scale));
1681 p_line->pi_underline_offset[ i ] =
1682 ( aOffset < 0 ) ? -aOffset : aOffset;
1683 p_line->pi_underline_thickness[ i ] =
1684 ( aSize < 0 ) ? -aSize : aSize;
1686 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1687 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1688 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1689 p_line->p_fg_bg_ratio[ i ] = 0x00;
1691 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1692 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1693 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1695 while( --i > *pi_start )
1697 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1700 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1704 if( psz_unicode == psz_unicode_start )
1706 if( b_first_on_line )
1708 msg_Warn( p_filter, "unbreakable string" );
1709 p_line->pp_glyphs[ i ] = NULL;
1710 return VLC_EGENERIC;
1712 *pi_pen_x = i_pen_x_start;
1714 p_line->i_width = line.xMax;
1715 p_line->i_height = __MAX( p_line->i_height,
1716 p_face->size->metrics.height >> 6 );
1717 p_line->pp_glyphs[ i ] = NULL;
1719 p_result->x = __MAX( p_result->x, line.xMax );
1720 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1721 i_yMax - i_yMin ) );
1728 *psz_unicode = '\n';
1730 psz_unicode = psz_unicode_start;
1731 *pi_pen_x = i_pen_x_start;
1739 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1740 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1742 i_previous = i_glyph_index;
1743 *pi_pen_x += p_face->glyph->advance.x >> 6;
1746 p_line->i_width = line.xMax;
1747 p_line->i_height = __MAX( p_line->i_height,
1748 p_face->size->metrics.height >> 6 );
1749 p_line->pp_glyphs[ i ] = NULL;
1751 p_result->x = __MAX( p_result->x, line.xMax );
1752 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1753 line.yMax - line.yMin ) );
1757 /* Get rid of any text processed - if necessary repositioning
1758 * at the start of a new line of text
1762 *psz_unicode_start = '\0';
1764 else if( psz_unicode > psz_unicode_start )
1766 for( i=0; psz_unicode[ i ]; i++ )
1767 psz_unicode_start[ i ] = psz_unicode[ i ];
1768 psz_unicode_start[ i ] = '\0';
1774 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1775 font_stack_t **p_fonts, int i_scale )
1778 char *psz_fontname = NULL;
1779 uint32_t i_font_color = 0xffffff;
1780 int i_font_alpha = 0;
1781 uint32_t i_karaoke_bg_color = 0x00ffffff;
1782 int i_font_size = 24;
1784 /* Default all attributes to the top font in the stack -- in case not
1785 * all attributes are specified in the sub-font
1787 if( VLC_SUCCESS == PeekFont( p_fonts,
1791 &i_karaoke_bg_color ))
1793 psz_fontname = strdup( psz_fontname );
1794 i_font_size = i_font_size * 1000 / i_scale;
1796 i_font_alpha = (i_font_color >> 24) & 0xff;
1797 i_font_color &= 0x00ffffff;
1799 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1801 char *psz_name = xml_ReaderName( p_xml_reader );
1802 char *psz_value = xml_ReaderValue( p_xml_reader );
1804 if( psz_name && psz_value )
1806 if( !strcasecmp( "face", psz_name ) )
1808 free( psz_fontname );
1809 psz_fontname = strdup( psz_value );
1811 else if( !strcasecmp( "size", psz_name ) )
1813 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1815 int i_value = atoi( psz_value );
1817 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1818 i_font_size += ( i_value * i_font_size ) / 10;
1819 else if( i_value < -5 )
1820 i_font_size = - i_value;
1821 else if( i_value > 5 )
1822 i_font_size = i_value;
1825 i_font_size = atoi( psz_value );
1827 else if( !strcasecmp( "color", psz_name ) &&
1828 ( psz_value[0] == '#' ) )
1830 i_font_color = strtol( psz_value + 1, NULL, 16 );
1831 i_font_color &= 0x00ffffff;
1833 else if( !strcasecmp( "alpha", psz_name ) &&
1834 ( psz_value[0] == '#' ) )
1836 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1837 i_font_alpha &= 0xff;
1843 rv = PushFont( p_fonts,
1845 i_font_size * i_scale / 1000,
1846 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1847 i_karaoke_bg_color );
1849 free( psz_fontname );
1854 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1855 uint32_t **psz_text_out, uint32_t *pi_runs,
1856 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1857 ft_style_t *p_style )
1859 uint32_t i_string_length = 0;
1861 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1862 *psz_text_out += i_string_length;
1864 if( ppp_styles && ppi_run_lengths )
1870 *ppp_styles = (ft_style_t **)
1871 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1873 else if( *pi_runs == 1 )
1875 *ppp_styles = (ft_style_t **)
1876 malloc( *pi_runs * sizeof( ft_style_t * ) );
1879 /* We have just malloc'ed this memory successfully -
1880 * *pi_runs HAS to be within the memory area of *ppp_styles */
1883 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1887 if( *ppi_run_lengths )
1889 *ppi_run_lengths = (uint32_t *)
1890 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1892 else if( *pi_runs == 1 )
1894 *ppi_run_lengths = (uint32_t *)
1895 malloc( *pi_runs * sizeof( uint32_t ) );
1898 /* same remarks here */
1899 if( *ppi_run_lengths )
1901 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1904 /* If we couldn't use the p_style argument due to memory allocation
1905 * problems above, release it here.
1907 if( p_style ) DeleteStyle( p_style );
1910 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1911 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1913 /* Karaoke tags _PRECEDE_ the text they specify a duration
1914 * for, therefore we are working out the length for the
1915 * previous tag, and first time through we have nothing
1917 if( pi_k_run_lengths )
1922 /* Work out how many characters are presently in the string
1924 for( i = 0; i < i_runs; i++ )
1925 i_chars += pi_run_lengths[ i ];
1927 /* Subtract away those we've already allocated to other
1930 for( i = 0; i < i_k_runs; i++ )
1931 i_chars -= pi_k_run_lengths[ i ];
1933 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1937 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1938 uint32_t **ppi_k_run_lengths,
1939 uint32_t **ppi_k_durations )
1941 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1943 char *psz_name = xml_ReaderName( p_xml_reader );
1944 char *psz_value = xml_ReaderValue( p_xml_reader );
1946 if( psz_name && psz_value &&
1947 !strcasecmp( "t", psz_name ) )
1949 if( ppi_k_durations && ppi_k_run_lengths )
1953 if( *ppi_k_durations )
1955 *ppi_k_durations = (uint32_t *)
1956 realloc( *ppi_k_durations,
1957 *pi_k_runs * sizeof( uint32_t ) );
1959 else if( *pi_k_runs == 1 )
1961 *ppi_k_durations = (uint32_t *)
1962 malloc( *pi_k_runs * sizeof( uint32_t ) );
1965 if( *ppi_k_run_lengths )
1967 *ppi_k_run_lengths = (uint32_t *)
1968 realloc( *ppi_k_run_lengths,
1969 *pi_k_runs * sizeof( uint32_t ) );
1971 else if( *pi_k_runs == 1 )
1973 *ppi_k_run_lengths = (uint32_t *)
1974 malloc( *pi_k_runs * sizeof( uint32_t ) );
1976 if( *ppi_k_durations )
1977 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1979 if( *ppi_k_run_lengths )
1980 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1988 static int ProcessNodes( filter_t *p_filter,
1989 xml_reader_t *p_xml_reader,
1990 text_style_t *p_font_style,
1995 uint32_t **ppi_run_lengths,
1996 ft_style_t ***ppp_styles,
1999 uint32_t *pi_k_runs,
2000 uint32_t **ppi_k_run_lengths,
2001 uint32_t **ppi_k_durations )
2003 int rv = VLC_SUCCESS;
2004 filter_sys_t *p_sys = p_filter->p_sys;
2005 uint32_t *psz_text_orig = psz_text;
2006 font_stack_t *p_fonts = NULL;
2010 char *psz_node = NULL;
2012 bool b_italic = false;
2013 bool b_bold = false;
2014 bool b_uline = false;
2016 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2017 i_scale = val.i_int;
2021 rv = PushFont( &p_fonts,
2022 p_font_style->psz_fontname,
2023 p_font_style->i_font_size * i_scale / 1000,
2024 (p_font_style->i_font_color & 0xffffff) |
2025 ((p_font_style->i_font_alpha & 0xff) << 24),
2026 (p_font_style->i_karaoke_background_color & 0xffffff) |
2027 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2029 if( p_font_style->i_style_flags & STYLE_BOLD )
2031 if( p_font_style->i_style_flags & STYLE_ITALIC )
2033 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2038 rv = PushFont( &p_fonts,
2044 if( rv != VLC_SUCCESS )
2047 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2049 switch ( xml_ReaderNodeType( p_xml_reader ) )
2051 case XML_READER_NONE:
2053 case XML_READER_ENDELEM:
2054 psz_node = xml_ReaderName( p_xml_reader );
2058 if( !strcasecmp( "font", psz_node ) )
2059 PopFont( &p_fonts );
2060 else if( !strcasecmp( "b", psz_node ) )
2062 else if( !strcasecmp( "i", psz_node ) )
2064 else if( !strcasecmp( "u", psz_node ) )
2070 case XML_READER_STARTELEM:
2071 psz_node = xml_ReaderName( p_xml_reader );
2074 if( !strcasecmp( "font", psz_node ) )
2075 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2076 else if( !strcasecmp( "b", psz_node ) )
2078 else if( !strcasecmp( "i", psz_node ) )
2080 else if( !strcasecmp( "u", psz_node ) )
2082 else if( !strcasecmp( "br", psz_node ) )
2084 SetupLine( p_filter, "\n", &psz_text,
2085 pi_runs, ppi_run_lengths, ppp_styles,
2086 GetStyleFromFontStack( p_sys,
2092 else if( !strcasecmp( "k", psz_node ) )
2094 /* Only valid in karaoke */
2097 if( *pi_k_runs > 0 )
2099 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2100 *pi_k_runs, *ppi_k_run_lengths );
2102 SetupKaraoke( p_xml_reader, pi_k_runs,
2103 ppi_k_run_lengths, ppi_k_durations );
2110 case XML_READER_TEXT:
2111 psz_node = xml_ReaderValue( p_xml_reader );
2114 /* Turn any multiple-whitespaces into single spaces */
2115 char *s = strpbrk( psz_node, "\t\r\n " );
2118 int i_whitespace = strspn( s, "\t\r\n " );
2120 if( i_whitespace > 1 )
2123 strlen( s ) - i_whitespace + 1 );
2126 s = strpbrk( s, "\t\r\n " );
2128 SetupLine( p_filter, psz_node, &psz_text,
2129 pi_runs, ppi_run_lengths, ppp_styles,
2130 GetStyleFromFontStack( p_sys,
2139 if( rv != VLC_SUCCESS )
2141 psz_text = psz_text_orig;
2147 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2148 *pi_k_runs, *ppi_k_run_lengths );
2151 *pi_len = psz_text - psz_text_orig;
2153 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2158 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2162 for( k=0; k < p_sys->i_font_attachments; k++ )
2164 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2166 FT_Face p_face = NULL;
2168 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2176 bool match = !strcasecmp( p_face->family_name,
2177 p_style->psz_fontname );
2179 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2180 match = match && p_style->b_bold;
2182 match = match && !p_style->b_bold;
2184 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2185 match = match && p_style->b_italic;
2187 match = match && !p_style->b_italic;
2195 FT_Done_Face( p_face );
2200 return VLC_EGENERIC;
2203 static int BuildDone( vlc_object_t *p_this, const char *psz_var,
2204 vlc_value_t oldval, vlc_value_t newval, void *param )
2209 ((filter_sys_t*)param)->b_fontconfig_ok = newval.b_bool;
2210 assert( newval.b_bool );
2214 static int ProcessLines( filter_t *p_filter,
2219 uint32_t *pi_run_lengths,
2220 ft_style_t **pp_styles,
2221 line_desc_t **pp_lines,
2223 FT_Vector *p_result,
2227 uint32_t *pi_k_run_lengths,
2228 uint32_t *pi_k_durations )
2230 filter_sys_t *p_sys = p_filter->p_sys;
2231 ft_style_t **pp_char_styles;
2232 int *p_new_positions = NULL;
2233 int8_t *p_levels = NULL;
2234 uint8_t *pi_karaoke_bar = NULL;
2238 /* Assign each character in the text string its style explicitly, so that
2239 * after the characters have been shuffled around by Fribidi, we can re-apply
2240 * the styles, and to simplify the calculation of runs within a line.
2242 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2243 if( !pp_char_styles )
2248 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2249 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2250 * we just won't be able to display the progress bar; at least we'll
2256 for( j = 0; j < i_runs; j++ )
2257 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2258 pp_char_styles[ i++ ] = pp_styles[ j ];
2260 #if defined(HAVE_FRIBIDI)
2262 ft_style_t **pp_char_styles_new;
2263 int *p_old_positions;
2264 uint32_t *p_fribidi_string;
2265 int start_pos, pos = 0;
2267 pp_char_styles_new = (ft_style_t **)
2268 malloc( i_len * sizeof( ft_style_t * ));
2270 p_fribidi_string = (uint32_t *)
2271 malloc( (i_len + 1) * sizeof(uint32_t) );
2272 p_old_positions = (int *)
2273 malloc( (i_len + 1) * sizeof( int ) );
2274 p_new_positions = (int *)
2275 malloc( (i_len + 1) * sizeof( int ) );
2276 p_levels = (int8_t *)
2277 malloc( (i_len + 1) * sizeof( int8_t ) );
2279 if( ! pp_char_styles_new ||
2280 ! p_fribidi_string ||
2281 ! p_old_positions ||
2282 ! p_new_positions ||
2285 msg_Err( p_filter, "out of memory" );
2287 free( p_old_positions );
2288 free( p_new_positions );
2289 free( p_fribidi_string );
2290 free( pp_char_styles_new );
2291 free( pi_karaoke_bar );
2293 free( pp_char_styles );
2297 /* Do bidi conversion line-by-line */
2300 while(pos < i_len) {
2301 if (psz_text[pos] != '\n')
2303 p_fribidi_string[pos] = psz_text[pos];
2304 pp_char_styles_new[pos] = pp_char_styles[pos];
2305 p_new_positions[pos] = pos;
2310 while(pos < i_len) {
2311 if (psz_text[pos] == '\n')
2315 if (pos > start_pos)
2317 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2318 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2319 pos - start_pos, &base_dir,
2320 (FriBidiChar*)p_fribidi_string + start_pos,
2321 p_new_positions + start_pos,
2323 p_levels + start_pos );
2324 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2326 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2327 p_old_positions[ j - start_pos ] ];
2328 p_new_positions[ j ] += start_pos;
2332 free( p_old_positions );
2333 free( pp_char_styles );
2334 pp_char_styles = pp_char_styles_new;
2335 psz_text = p_fribidi_string;
2336 p_fribidi_string[ i_len ] = 0;
2339 /* Work out the karaoke */
2340 if( pi_karaoke_bar )
2342 int64_t i_last_duration = 0;
2343 int64_t i_duration = 0;
2344 int64_t i_start_pos = 0;
2345 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2347 for( k = 0; k< i_k_runs; k++ )
2349 double fraction = 0.0;
2351 i_duration += pi_k_durations[ k ];
2353 if( i_duration < i_elapsed )
2355 /* Completely finished this run-length -
2356 * let it render normally */
2360 else if( i_elapsed < i_last_duration )
2362 /* Haven't got up to this segment yet -
2363 * render it completely in karaoke BG mode */
2369 /* Partway through this run */
2371 fraction = (double)(i_elapsed - i_last_duration) /
2372 (double)pi_k_durations[ k ];
2374 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2376 double shade = pi_k_run_lengths[ k ] * fraction;
2378 if( p_new_positions )
2379 j = p_new_positions[ i_start_pos + i ];
2381 j = i_start_pos + i;
2383 if( i < (uint32_t)shade )
2384 pi_karaoke_bar[ j ] = 0xff;
2385 else if( (double)i > shade )
2386 pi_karaoke_bar[ j ] = 0x00;
2389 shade -= (int)shade;
2390 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2391 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2395 i_last_duration = i_duration;
2396 i_start_pos += pi_k_run_lengths[ k ];
2400 free( p_new_positions );
2402 FT_Vector tmp_result;
2404 line_desc_t *p_line = NULL;
2405 line_desc_t *p_prev = NULL;
2411 p_result->x = p_result->y = 0;
2412 tmp_result.x = tmp_result.y = 0;
2415 for( k = 0; k <= (uint32_t) i_len; k++ )
2417 if( ( k == (uint32_t) i_len ) ||
2419 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2421 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2423 /* End of the current style run */
2424 FT_Face p_face = NULL;
2427 /* Look for a match amongst our attachments first */
2428 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2430 if( ! p_face && p_sys->b_fontconfig_ok )
2433 vlc_mutex_lock( &p_sys->fontconfig_lock );
2435 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2436 p_style->psz_fontname,
2440 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2442 if( psz_fontfile && ! *psz_fontfile )
2444 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2445 " so using default font", p_style->psz_fontname,
2446 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2447 (p_style->b_bold ? "(Bold)" :
2448 (p_style->b_italic ? "(Italic)" : ""))) );
2449 free( psz_fontfile );
2450 psz_fontfile = NULL;
2455 if( FT_New_Face( p_sys->p_library,
2456 psz_fontfile, i_idx, &p_face ) )
2458 free( psz_fontfile );
2459 free( pp_char_styles );
2460 #if defined(HAVE_FRIBIDI)
2463 free( pi_karaoke_bar );
2464 return VLC_EGENERIC;
2466 free( psz_fontfile );
2470 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2472 /* We've loaded a font face which is unhelpful for actually
2473 * rendering text - fallback to the default one.
2475 FT_Done_Face( p_face );
2479 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2480 ft_encoding_unicode ) ||
2481 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2482 p_style->i_font_size ) )
2484 if( p_face ) FT_Done_Face( p_face );
2485 free( pp_char_styles );
2486 #if defined(HAVE_FRIBIDI)
2489 free( pi_karaoke_bar );
2490 return VLC_EGENERIC;
2492 p_sys->i_use_kerning =
2493 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2496 uint32_t *psz_unicode = (uint32_t *)
2497 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2500 msg_Err( p_filter, "out of memory" );
2501 if( p_face ) FT_Done_Face( p_face );
2502 free( pp_char_styles );
2503 free( psz_unicode );
2504 #if defined(HAVE_FRIBIDI)
2507 free( pi_karaoke_bar );
2510 memcpy( psz_unicode, psz_text + i_prev,
2511 sizeof( uint32_t ) * ( k - i_prev ) );
2512 psz_unicode[ k - i_prev ] = 0;
2513 while( *psz_unicode )
2517 if( !(p_line = NewLine( i_len - i_prev)) )
2519 msg_Err( p_filter, "out of memory" );
2520 if( p_face ) FT_Done_Face( p_face );
2521 free( pp_char_styles );
2522 free( psz_unicode );
2523 #if defined(HAVE_FRIBIDI)
2526 free( pi_karaoke_bar );
2529 /* New Color mode only works in YUVA rendering mode --
2530 * (RGB mode has palette constraints on it). We therefore
2531 * need to populate the legacy colour fields also.
2533 p_line->b_new_color_mode = true;
2534 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2535 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2536 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2537 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2538 p_line->p_next = NULL;
2540 i_pen_y += tmp_result.y;
2544 if( p_prev ) p_prev->p_next = p_line;
2545 else *pp_lines = p_line;
2548 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2549 p_style->i_font_color, p_style->b_underline,
2550 p_style->i_karaoke_bg_color,
2551 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2552 &tmp_result ) != VLC_SUCCESS )
2554 if( p_face ) FT_Done_Face( p_face );
2555 free( pp_char_styles );
2556 free( psz_unicode );
2557 #if defined(HAVE_FRIBIDI)
2560 free( pi_karaoke_bar );
2561 return VLC_EGENERIC;
2566 p_result->x = __MAX( p_result->x, tmp_result.x );
2567 p_result->y += tmp_result.y;
2572 if( *psz_unicode == '\n')
2576 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2578 *c_ptr = *(c_ptr+1);
2583 free( psz_unicode );
2584 if( p_face ) FT_Done_Face( p_face );
2588 free( pp_char_styles );
2589 #if defined(HAVE_FRIBIDI)
2594 p_result->x = __MAX( p_result->x, tmp_result.x );
2595 p_result->y += tmp_result.y;
2598 if( pi_karaoke_bar )
2601 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2603 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2605 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2609 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2611 /* 100% BG colour will render faster if we
2612 * instead make it 100% FG colour, so leave
2613 * the ratio alone and copy the value across
2615 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2619 if( pi_karaoke_bar[ i ] & 0x80 )
2621 /* Swap Left and Right sides over for Right aligned
2622 * language text (eg. Arabic, Hebrew)
2624 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2626 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2627 p_line->p_bg_rgb[ k ] = i_tmp;
2629 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2632 /* Jump over the '\n' at the line-end */
2635 free( pi_karaoke_bar );
2641 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2642 subpicture_region_t *p_region_in )
2644 int rv = VLC_SUCCESS;
2645 stream_t *p_sub = NULL;
2646 xml_t *p_xml = NULL;
2647 xml_reader_t *p_xml_reader = NULL;
2649 if( !p_region_in || !p_region_in->psz_html )
2650 return VLC_EGENERIC;
2652 /* Reset the default fontsize in case screen metrics have changed */
2653 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2655 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2656 (uint8_t *) p_region_in->psz_html,
2657 strlen( p_region_in->psz_html ),
2661 p_xml = xml_Create( p_filter );
2664 bool b_karaoke = false;
2666 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2669 /* Look for Root Node */
2670 if( xml_ReaderRead( p_xml_reader ) == 1 )
2672 char *psz_node = xml_ReaderName( p_xml_reader );
2674 if( !strcasecmp( "karaoke", psz_node ) )
2676 /* We're going to have to render the text a number
2677 * of times to show the progress marker on the text.
2679 var_SetBool( p_filter, "text-rerender", true );
2682 else if( !strcasecmp( "text", psz_node ) )
2688 /* Only text and karaoke tags are supported */
2689 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2690 xml_ReaderDelete( p_xml, p_xml_reader );
2691 p_xml_reader = NULL;
2703 uint32_t i_runs = 0;
2704 uint32_t i_k_runs = 0;
2705 uint32_t *pi_run_lengths = NULL;
2706 uint32_t *pi_k_run_lengths = NULL;
2707 uint32_t *pi_k_durations = NULL;
2708 ft_style_t **pp_styles = NULL;
2710 line_desc_t *p_lines = NULL;
2712 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2713 sizeof( uint32_t ) );
2718 rv = ProcessNodes( p_filter, p_xml_reader,
2719 p_region_in->p_style, psz_text, &i_len,
2720 &i_runs, &pi_run_lengths, &pp_styles,
2721 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2724 p_region_out->i_x = p_region_in->i_x;
2725 p_region_out->i_y = p_region_in->i_y;
2727 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2729 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2730 pi_run_lengths, pp_styles, &p_lines, &result,
2731 b_karaoke, i_k_runs, pi_k_run_lengths,
2735 for( k=0; k<i_runs; k++)
2736 DeleteStyle( pp_styles[k] );
2738 free( pi_run_lengths );
2741 /* Don't attempt to render text that couldn't be layed out
2744 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2746 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2748 Render( p_filter, p_region_out, p_lines,
2749 result.x, result.y );
2753 RenderYUVA( p_filter, p_region_out, p_lines,
2754 result.x, result.y );
2758 FreeLines( p_lines );
2760 xml_ReaderDelete( p_xml, p_xml_reader );
2762 xml_Delete( p_xml );
2764 stream_Delete( p_sub );
2770 static char* FontConfig_Select( FcConfig* priv, const char* family,
2771 bool b_bold, bool b_italic, int *i_idx )
2774 FcPattern *pat, *p_pat;
2778 pat = FcPatternCreate();
2779 if (!pat) return NULL;
2781 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2782 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2783 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2784 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2786 FcDefaultSubstitute( pat );
2788 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2790 FcPatternDestroy( pat );
2794 p_pat = FcFontMatch( priv, pat, &result );
2795 FcPatternDestroy( pat );
2796 if( !p_pat ) return NULL;
2798 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2799 || ( val_b != FcTrue ) )
2801 FcPatternDestroy( p_pat );
2804 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2809 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2811 FcPatternDestroy( p_pat );
2816 if( strcasecmp((const char*)val_s, family ) != 0 )
2817 msg_Warn( p_filter, "fontconfig: selected font family is not"
2818 "the requested one: '%s' != '%s'\n",
2819 (const char*)val_s, family );
2822 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2824 FcPatternDestroy( p_pat );
2828 FcPatternDestroy( p_pat );
2829 return strdup( (const char*)val_s );
2833 static void FreeLine( line_desc_t *p_line )
2836 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2838 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2840 free( p_line->pp_glyphs );
2841 free( p_line->p_glyph_pos );
2842 free( p_line->p_fg_rgb );
2843 free( p_line->p_bg_rgb );
2844 free( p_line->p_fg_bg_ratio );
2845 free( p_line->pi_underline_offset );
2846 free( p_line->pi_underline_thickness );
2850 static void FreeLines( line_desc_t *p_lines )
2852 line_desc_t *p_line, *p_next;
2854 if( !p_lines ) return;
2856 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2858 p_next = p_line->p_next;
2863 static line_desc_t *NewLine( int i_count )
2865 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2867 if( !p_line ) return NULL;
2868 p_line->i_height = 0;
2869 p_line->i_width = 0;
2870 p_line->p_next = NULL;
2872 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2873 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2874 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2875 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2876 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2877 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2878 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2879 if( ( p_line->pp_glyphs == NULL ) ||
2880 ( p_line->p_glyph_pos == NULL ) ||
2881 ( p_line->p_fg_rgb == NULL ) ||
2882 ( p_line->p_bg_rgb == NULL ) ||
2883 ( p_line->p_fg_bg_ratio == NULL ) ||
2884 ( p_line->pi_underline_offset == NULL ) ||
2885 ( p_line->pi_underline_thickness == NULL ) )
2887 free( p_line->pi_underline_thickness );
2888 free( p_line->pi_underline_offset );
2889 free( p_line->p_fg_rgb );
2890 free( p_line->p_bg_rgb );
2891 free( p_line->p_fg_bg_ratio );
2892 free( p_line->p_glyph_pos );
2893 free( p_line->pp_glyphs );
2897 p_line->pp_glyphs[0] = NULL;
2898 p_line->b_new_color_mode = false;
2903 static int GetFontSize( filter_t *p_filter )
2905 filter_sys_t *p_sys = p_filter->p_sys;
2909 if( p_sys->i_default_font_size )
2911 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2912 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2914 i_size = p_sys->i_default_font_size;
2918 var_Get( p_filter, "freetype-rel-fontsize", &val );
2919 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2920 p_filter->p_sys->i_display_height =
2921 p_filter->fmt_out.video.i_height;
2925 msg_Warn( p_filter, "invalid fontsize, using 12" );
2926 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2927 i_size = 12 * val.i_int / 1000;
2934 static int SetFontSize( filter_t *p_filter, int i_size )
2936 filter_sys_t *p_sys = p_filter->p_sys;
2940 i_size = GetFontSize( p_filter );
2942 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2945 p_sys->i_font_size = i_size;
2947 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2949 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2950 return VLC_EGENERIC;
2956 static void YUVFromRGB( uint32_t i_argb,
2957 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2959 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2960 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2961 int i_blue = ( i_argb & 0x000000ff );
2963 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2964 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2965 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2966 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2967 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2968 -585 * i_blue + 4096 + 1048576) >> 13, 240);