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 *,
92 vlc_bool_t, vlc_bool_t, int * );
93 static int CheckIfFontBuildComplete( filter_t *p_filter );
95 static line_desc_t *NewLine( int );
97 static int GetFontSize( filter_t *p_filter );
98 static int SetFontSize( filter_t *, int );
99 static void YUVFromRGB( uint32_t i_argb,
100 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
102 /*****************************************************************************
104 *****************************************************************************/
105 #define FONT_TEXT N_("Font")
106 #define FONT_LONGTEXT N_("Filename for the font you want to use")
107 #define FONTSIZE_TEXT N_("Font size in pixels")
108 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
109 "that will be rendered on the video. " \
110 "If set to something different than 0 this option will override the " \
111 "relative font size." )
112 #define OPACITY_TEXT N_("Opacity")
113 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
114 "text that will be rendered on the video. 0 = transparent, " \
115 "255 = totally opaque. " )
116 #define COLOR_TEXT N_("Text default color")
117 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
118 "the video. This must be an hexadecimal (like HTML colors). The first two "\
119 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
120 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
121 #define FONTSIZER_TEXT N_("Relative font size")
122 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
123 "fonts that will be rendered on the video. If absolute font size is set, "\
124 "relative size will be overriden." )
126 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
127 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
128 N_("Large"), N_("Larger") };
129 #define YUVP_TEXT N_("Use YUVP renderer")
130 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
131 "This option is only needed if you want to encode into DVB subtitles" )
132 #define EFFECT_TEXT N_("Font Effect")
133 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
134 "text to improve its readability." )
136 #define EFFECT_BACKGROUND 1
137 #define EFFECT_OUTLINE 2
138 #define EFFECT_OUTLINE_FAT 3
140 static int pi_effects[] = { 1, 2, 3 };
141 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
143 static int pi_color_values[] = {
144 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
145 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
146 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
148 static const char *ppsz_color_descriptions[] = {
149 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
150 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
151 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
154 set_shortname( _("Text renderer"));
155 set_description( _("Freetype2 font renderer") );
156 set_category( CAT_VIDEO );
157 set_subcategory( SUBCAT_VIDEO_SUBPIC );
159 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
162 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
163 FONTSIZE_LONGTEXT, VLC_TRUE );
165 /* opacity valid on 0..255, with default 255 = fully opaque */
166 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
167 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
169 /* hook to the color values list, with default 0x00ffffff = white */
170 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
171 COLOR_LONGTEXT, VLC_FALSE );
172 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
174 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
175 FONTSIZER_LONGTEXT, VLC_FALSE );
176 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
177 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
178 EFFECT_LONGTEXT, VLC_FALSE );
179 change_integer_list( pi_effects, ppsz_effects_text, 0 );
181 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
182 YUVP_LONGTEXT, VLC_TRUE );
183 set_capability( "text renderer", 100 );
184 add_shortcut( "text" );
185 set_callbacks( Create, Destroy );
190 /** NULL-terminated list of glyphs making the string */
191 FT_BitmapGlyph *pp_glyphs;
192 /** list of relative positions for the glyphs */
193 FT_Vector *p_glyph_pos;
194 /** list of RGB information for styled text
195 * -- if the rendering mode supports it (RenderYUVA) and
196 * b_new_color_mode is set, then it becomes possible to
197 * have multicoloured text within the subtitles. */
200 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
201 vlc_bool_t b_new_color_mode;
202 /** underline information -- only supplied if text should be underlined */
203 uint16_t *pi_underline_offset;
204 uint16_t *pi_underline_thickness;
208 int i_red, i_green, i_blue;
214 typedef struct font_stack_t font_stack_t;
219 uint32_t i_color; /* ARGB */
220 uint32_t i_karaoke_bg_color; /* ARGB */
222 font_stack_t *p_next;
228 uint32_t i_font_color; /* ARGB */
229 uint32_t i_karaoke_bg_color; /* ARGB */
232 vlc_bool_t b_underline;
236 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
237 static void FreeLines( line_desc_t * );
238 static void FreeLine( line_desc_t * );
239 #ifdef HAVE_FONTCONFIG
240 static void FontBuilder( vlc_object_t *p_this);
243 /*****************************************************************************
244 * filter_sys_t: freetype local data
245 *****************************************************************************
246 * This structure is part of the video output thread descriptor.
247 * It describes the freetype specific properties of an output thread.
248 *****************************************************************************/
251 FT_Library p_library; /* handle to library */
252 FT_Face p_face; /* handle to face object */
253 vlc_bool_t i_use_kerning;
254 uint8_t i_font_opacity;
259 int i_default_font_size;
260 int i_display_height;
261 #ifdef HAVE_FONTCONFIG
262 FcConfig *p_fontconfig;
263 vlc_bool_t b_fontconfig_ok;
264 vlc_mutex_t fontconfig_lock;
267 input_attachment_t **pp_font_attachments;
268 int i_font_attachments;
271 /*****************************************************************************
272 * Create: allocates osd-text video thread output method
273 *****************************************************************************
274 * This function allocates and initializes a Clone vout method.
275 *****************************************************************************/
276 static int Create( vlc_object_t *p_this )
278 filter_t *p_filter = (filter_t *)p_this;
280 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 if( psz_fontfile ) 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_filter, &p_sys->fontconfig_lock );
367 p_sys->b_fontconfig_ok = VLC_FALSE;
368 p_sys->p_fontconfig = NULL;
370 /* Check for an existing Fontbuilder thread */
371 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 = "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", VLC_FALSE );
395 if( vlc_thread_create( p_fontbuilder,
398 VLC_THREAD_PRIORITY_LOW,
401 /* Don't destroy the fontconfig object - we won't be able to do
402 * italics or bold or change the font face, but we will still
403 * be able to do underline and change the font size.
405 msg_Warn( p_filter, "fontconfig database builder thread can't "
406 "be launched. Font styling support will be limited." );
411 vlc_object_release( p_fontbuilder );
416 vlc_object_release( p_fontbuilder );
418 vlc_mutex_unlock( lock );
422 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
424 var_Get( p_filter, "freetype-fontsize", &val );
425 p_sys->i_default_font_size = val.i_int;
426 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
428 if( psz_fontfile ) free( psz_fontfile );
430 p_sys->pp_font_attachments = NULL;
431 p_sys->i_font_attachments = 0;
433 p_filter->pf_render_text = RenderText;
434 #ifdef HAVE_FONTCONFIG
435 p_filter->pf_render_html = RenderHtml;
437 p_filter->pf_render_html = NULL;
440 LoadFontsFromAttachments( p_filter );
445 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
446 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
447 if( psz_fontfile ) free( psz_fontfile );
452 /*****************************************************************************
453 * Destroy: destroy Clone video thread output method
454 *****************************************************************************
455 * Clean up all data and library connections
456 *****************************************************************************/
457 static void Destroy( vlc_object_t *p_this )
459 filter_t *p_filter = (filter_t *)p_this;
460 filter_sys_t *p_sys = p_filter->p_sys;
462 if( p_sys->pp_font_attachments )
466 for( k = 0; k < p_sys->i_font_attachments; k++ )
468 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
471 free( p_sys->pp_font_attachments );
474 #ifdef HAVE_FONTCONFIG
475 vlc_mutex_destroy( &p_sys->fontconfig_lock );
477 if( p_sys->p_fontconfig )
479 FcConfigDestroy( p_sys->p_fontconfig );
480 p_sys->p_fontconfig = NULL;
482 /* FcFini asserts calling the subfunction FcCacheFini()
483 * even if no other library functions have been made since FcInit(),
484 * so don't call it. */
486 FT_Done_Face( p_sys->p_face );
487 FT_Done_FreeType( p_sys->p_library );
491 #ifdef HAVE_FONTCONFIG
493 static void FontBuilder( vlc_object_t *p_this )
495 FcConfig *p_fontconfig = FcInitLoadConfig();
498 vlc_thread_ready( p_this );
504 msg_Dbg( p_this, "Building font database..." );
506 if(! FcConfigBuildFonts( p_fontconfig ))
508 /* Don't destroy the fontconfig object - we won't be able to do
509 * italics or bold or change the font face, but we will still
510 * be able to do underline and change the font size.
512 msg_Err( p_this, "fontconfig database can't be built. "
513 "Font styling won't be available" );
517 msg_Dbg( p_this, "Finished building font database." );
518 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
520 lock = var_AcquireMutex( "fontbuilder" );
521 var_SetBool( p_this, "build-done", VLC_TRUE );
523 FcConfigDestroy( p_fontconfig );
524 vlc_mutex_unlock( lock );
530 /*****************************************************************************
531 * Make any TTF/OTF fonts present in the attachments of the media file
532 * and store them for later use by the FreeType Engine
533 *****************************************************************************/
534 static int LoadFontsFromAttachments( filter_t *p_filter )
536 filter_sys_t *p_sys = p_filter->p_sys;
537 input_thread_t *p_input;
538 input_attachment_t **pp_attachments;
539 int i_attachments_cnt;
541 int rv = VLC_SUCCESS;
543 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
547 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
549 vlc_object_release(p_input);
553 p_sys->i_font_attachments = 0;
554 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
555 if(! p_sys->pp_font_attachments )
558 for( k = 0; k < i_attachments_cnt; k++ )
560 input_attachment_t *p_attach = pp_attachments[k];
562 if( p_sys->pp_font_attachments )
564 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
565 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
566 ( p_attach->i_data > 0 ) &&
567 ( p_attach->p_data != NULL ) )
569 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
573 vlc_input_attachment_Delete( p_attach );
578 vlc_input_attachment_Delete( p_attach );
581 free( pp_attachments );
583 vlc_object_release(p_input);
588 /*****************************************************************************
589 * Render: place string in picture
590 *****************************************************************************
591 * This function merges the previously rendered freetype glyphs into a picture
592 *****************************************************************************/
593 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
594 line_desc_t *p_line, int i_width, int i_height )
596 static uint8_t pi_gamma[16] =
597 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
598 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
602 int i, x, y, i_pitch;
603 uint8_t i_y; /* YUV values, derived from incoming RGB */
605 subpicture_region_t *p_region_tmp;
607 /* Create a new subpicture region */
608 memset( &fmt, 0, sizeof(video_format_t) );
609 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
611 fmt.i_width = fmt.i_visible_width = i_width + 4;
612 fmt.i_height = fmt.i_visible_height = i_height + 4;
613 if( p_region->fmt.i_visible_width > 0 )
614 fmt.i_visible_width = p_region->fmt.i_visible_width;
615 if( p_region->fmt.i_visible_height > 0 )
616 fmt.i_visible_height = p_region->fmt.i_visible_height;
617 fmt.i_x_offset = fmt.i_y_offset = 0;
618 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
621 msg_Err( p_filter, "cannot allocate SPU region" );
625 p_region->fmt = p_region_tmp->fmt;
626 p_region->picture = p_region_tmp->picture;
627 free( p_region_tmp );
629 /* Calculate text color components */
630 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
631 25 * p_line->i_blue + 128) >> 8) + 16;
632 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
633 112 * p_line->i_blue + 128) >> 8) + 128;
634 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
635 18 * p_line->i_blue + 128) >> 8) + 128;
638 fmt.p_palette->i_entries = 16;
639 for( i = 0; i < 8; i++ )
641 fmt.p_palette->palette[i][0] = 0;
642 fmt.p_palette->palette[i][1] = 0x80;
643 fmt.p_palette->palette[i][2] = 0x80;
644 fmt.p_palette->palette[i][3] = pi_gamma[i];
645 fmt.p_palette->palette[i][3] =
646 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
648 for( i = 8; i < fmt.p_palette->i_entries; i++ )
650 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
651 fmt.p_palette->palette[i][1] = i_u;
652 fmt.p_palette->palette[i][2] = i_v;
653 fmt.p_palette->palette[i][3] = pi_gamma[i];
654 fmt.p_palette->palette[i][3] =
655 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
658 p_dst = p_region->picture.Y_PIXELS;
659 i_pitch = p_region->picture.Y_PITCH;
661 /* Initialize the region pixels */
662 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
664 for( ; p_line != NULL; p_line = p_line->p_next )
666 int i_glyph_tmax = 0;
667 int i_bitmap_offset, i_offset, i_align_offset = 0;
668 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
670 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
671 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
674 if( p_line->i_width < i_width )
676 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
678 i_align_offset = i_width - p_line->i_width;
680 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
682 i_align_offset = ( i_width - p_line->i_width ) / 2;
686 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
688 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
690 i_offset = ( p_line->p_glyph_pos[ i ].y +
691 i_glyph_tmax - p_glyph->top + 2 ) *
692 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
695 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
697 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
699 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
701 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
708 /* Outlining (find something better than nearest neighbour filtering ?) */
711 uint8_t *p_dst = p_region->picture.Y_PIXELS;
712 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
713 uint8_t left, current;
715 for( y = 1; y < (int)fmt.i_height - 1; y++ )
717 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
718 p_dst += p_region->picture.Y_PITCH;
721 for( x = 1; x < (int)fmt.i_width - 1; x++ )
724 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
725 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;
729 memset( p_top, 0, fmt.i_width );
735 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
736 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
737 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
738 int i_glyph_tmax, int i_align_offset,
739 uint8_t i_y, uint8_t i_u, uint8_t i_v,
740 subpicture_region_t *p_region)
744 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
746 p_dst_y = p_region->picture.Y_PIXELS;
747 p_dst_u = p_region->picture.U_PIXELS;
748 p_dst_v = p_region->picture.V_PIXELS;
749 p_dst_a = p_region->picture.A_PIXELS;
750 i_pitch = p_region->picture.A_PITCH;
752 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
753 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
755 for( y = 0; y < i_line_thickness; y++ )
757 int i_extra = p_this_glyph->bitmap.width;
761 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
762 (p_this_glyph_pos->x + p_this_glyph->left);
764 for( x = 0; x < i_extra; x++ )
766 vlc_bool_t b_ok = VLC_TRUE;
768 /* break the underline around the tails of any glyphs which cross it */
769 for( z = x - i_line_thickness;
770 z < x + i_line_thickness && b_ok;
773 if( p_next_glyph && ( z >= i_extra ) )
775 int i_row = i_line_offset + p_next_glyph->top + y;
777 if( ( p_next_glyph->bitmap.rows > i_row ) &&
778 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
783 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
785 int i_row = i_line_offset + p_this_glyph->top + y;
787 if( ( p_this_glyph->bitmap.rows > i_row ) &&
788 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
797 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
798 p_dst_u[i_offset+x] = i_u;
799 p_dst_v[i_offset+x] = i_v;
800 p_dst_a[i_offset+x] = 255;
807 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
809 uint8_t *p_dst = p_region->picture.A_PIXELS;
810 int i_pitch = p_region->picture.A_PITCH;
813 for( ; p_line != NULL; p_line = p_line->p_next )
815 int i_glyph_tmax=0, i = 0;
816 int i_bitmap_offset, i_offset, i_align_offset = 0;
817 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
819 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
820 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
823 if( p_line->i_width < i_width )
825 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
827 i_align_offset = i_width - p_line->i_width;
829 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
831 i_align_offset = ( i_width - p_line->i_width ) / 2;
835 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
837 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
839 i_offset = ( p_line->p_glyph_pos[ i ].y +
840 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
841 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
842 i_align_offset +xoffset;
844 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
846 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
848 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
849 if( p_dst[i_offset+x] <
850 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
852 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
861 /*****************************************************************************
862 * Render: place string in picture
863 *****************************************************************************
864 * This function merges the previously rendered freetype glyphs into a picture
865 *****************************************************************************/
866 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
867 line_desc_t *p_line, int i_width, int i_height )
869 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
871 int i, x, y, i_pitch, i_alpha;
872 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
873 subpicture_region_t *p_region_tmp;
875 if( i_width == 0 || i_height == 0 )
878 /* Create a new subpicture region */
879 memset( &fmt, 0, sizeof(video_format_t) );
880 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
882 fmt.i_width = fmt.i_visible_width = i_width + 6;
883 fmt.i_height = fmt.i_visible_height = i_height + 6;
884 if( p_region->fmt.i_visible_width > 0 )
885 fmt.i_visible_width = p_region->fmt.i_visible_width;
886 if( p_region->fmt.i_visible_height > 0 )
887 fmt.i_visible_height = p_region->fmt.i_visible_height;
888 fmt.i_x_offset = fmt.i_y_offset = 0;
889 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
892 msg_Err( p_filter, "cannot allocate SPU region" );
896 p_region->fmt = p_region_tmp->fmt;
897 p_region->picture = p_region_tmp->picture;
898 free( p_region_tmp );
900 /* Calculate text color components */
901 YUVFromRGB( (p_line->i_red << 16) |
902 (p_line->i_green << 8) |
905 i_alpha = p_line->i_alpha;
907 p_dst_y = p_region->picture.Y_PIXELS;
908 p_dst_u = p_region->picture.U_PIXELS;
909 p_dst_v = p_region->picture.V_PIXELS;
910 p_dst_a = p_region->picture.A_PIXELS;
911 i_pitch = p_region->picture.A_PITCH;
913 /* Initialize the region pixels */
914 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
916 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
917 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
918 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
919 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
923 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
924 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
925 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
926 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
928 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
929 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
931 DrawBlack( p_line, i_width, p_region, 0, 0);
932 DrawBlack( p_line, i_width, p_region, -1, 0);
933 DrawBlack( p_line, i_width, p_region, 0, -1);
934 DrawBlack( p_line, i_width, p_region, 1, 0);
935 DrawBlack( p_line, i_width, p_region, 0, 1);
938 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
940 DrawBlack( p_line, i_width, p_region, -1, -1);
941 DrawBlack( p_line, i_width, p_region, -1, 1);
942 DrawBlack( p_line, i_width, p_region, 1, -1);
943 DrawBlack( p_line, i_width, p_region, 1, 1);
945 DrawBlack( p_line, i_width, p_region, -2, 0);
946 DrawBlack( p_line, i_width, p_region, 0, -2);
947 DrawBlack( p_line, i_width, p_region, 2, 0);
948 DrawBlack( p_line, i_width, p_region, 0, 2);
950 DrawBlack( p_line, i_width, p_region, -2, -2);
951 DrawBlack( p_line, i_width, p_region, -2, 2);
952 DrawBlack( p_line, i_width, p_region, 2, -2);
953 DrawBlack( p_line, i_width, p_region, 2, 2);
955 DrawBlack( p_line, i_width, p_region, -3, 0);
956 DrawBlack( p_line, i_width, p_region, 0, -3);
957 DrawBlack( p_line, i_width, p_region, 3, 0);
958 DrawBlack( p_line, i_width, p_region, 0, 3);
961 for( ; p_line != NULL; p_line = p_line->p_next )
963 int i_glyph_tmax = 0;
964 int i_bitmap_offset, i_offset, i_align_offset = 0;
965 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
967 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
968 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
971 if( p_line->i_width < i_width )
973 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
975 i_align_offset = i_width - p_line->i_width;
977 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
979 i_align_offset = ( i_width - p_line->i_width ) / 2;
983 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
985 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
987 i_offset = ( p_line->p_glyph_pos[ i ].y +
988 i_glyph_tmax - p_glyph->top + 3 ) *
989 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
992 if( p_line->b_new_color_mode )
994 /* Every glyph can (and in fact must) have its own color */
995 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
998 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1000 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1002 uint8_t i_y_local = i_y;
1003 uint8_t i_u_local = i_u;
1004 uint8_t i_v_local = i_v;
1006 if( p_line->p_fg_bg_ratio != 0x00 )
1008 int i_split = p_glyph->bitmap.width *
1009 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1013 YUVFromRGB( p_line->p_bg_rgb[ i ],
1014 &i_y_local, &i_u_local, &i_v_local );
1018 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1020 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1021 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1023 p_dst_u[i_offset+x] = i_u;
1024 p_dst_v[i_offset+x] = i_v;
1026 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1027 p_dst_a[i_offset+x] = 0xff;
1030 i_offset += i_pitch;
1033 if( p_line->pi_underline_thickness[ i ] )
1035 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1036 p_line->pi_underline_offset[ i ],
1037 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1038 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1039 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1040 i_glyph_tmax, i_align_offset,
1047 /* Apply the alpha setting */
1048 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1049 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1055 * This function renders a text subpicture region into another one.
1056 * It also calculates the size needed for this string, and renders the
1057 * needed glyphs into memory. It is used as pf_add_string callback in
1058 * the vout method by this module
1060 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1061 subpicture_region_t *p_region_in )
1063 filter_sys_t *p_sys = p_filter->p_sys;
1064 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1065 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1066 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1067 int i_string_length;
1069 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1070 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1080 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1081 psz_string = p_region_in->psz_text;
1082 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1084 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1085 i_scale = val.i_int;
1087 if( p_region_in->p_style )
1089 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1090 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1091 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1095 i_font_color = p_sys->i_font_color;
1096 i_font_alpha = 255 - p_sys->i_font_opacity;
1097 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1100 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1101 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1102 SetFontSize( p_filter, i_font_size );
1104 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1105 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1106 i_blue = i_font_color & 0x000000FF;
1108 result.x = result.y = 0;
1109 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1111 psz_unicode = psz_unicode_orig =
1112 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1113 if( psz_unicode == NULL )
1115 msg_Err( p_filter, "out of memory" );
1118 #if defined(WORDS_BIGENDIAN)
1119 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1121 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1123 if( iconv_handle == (vlc_iconv_t)-1 )
1125 msg_Warn( p_filter, "unable to do conversion" );
1131 const char *p_in_buffer = psz_string;
1132 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1133 i_in_bytes = strlen( psz_string );
1134 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1135 i_out_bytes_left = i_out_bytes;
1136 p_out_buffer = (char *)psz_unicode;
1137 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1139 &p_out_buffer, &i_out_bytes_left );
1141 vlc_iconv_close( iconv_handle );
1145 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1146 "bytes left %u", (unsigned)i_in_bytes );
1149 *(uint32_t*)p_out_buffer = 0;
1150 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1153 #if defined(HAVE_FRIBIDI)
1155 uint32_t *p_fribidi_string;
1156 int32_t start_pos, pos = 0;
1158 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1159 if( !p_fribidi_string )
1161 msg_Err( p_filter, "out of memory" );
1165 /* Do bidi conversion line-by-line */
1166 while( pos < i_string_length )
1168 while( pos < i_string_length )
1170 i_char = psz_unicode[pos];
1171 if (i_char != '\r' && i_char != '\n')
1173 p_fribidi_string[pos] = i_char;
1177 while( pos < i_string_length )
1179 i_char = psz_unicode[pos];
1180 if (i_char == '\r' || i_char == '\n')
1184 if (pos > start_pos)
1186 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1187 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1190 (FriBidiChar*)p_fribidi_string + start_pos,
1195 free( psz_unicode_orig );
1196 psz_unicode = psz_unicode_orig = p_fribidi_string;
1197 p_fribidi_string[ i_string_length ] = 0;
1201 /* Calculate relative glyph positions and a bounding box for the
1203 if( !(p_line = NewLine( strlen( psz_string ))) )
1205 msg_Err( p_filter, "out of memory" );
1209 i_pen_x = i_pen_y = 0;
1211 psz_line_start = psz_unicode;
1213 #define face p_sys->p_face
1214 #define glyph face->glyph
1216 while( *psz_unicode )
1218 i_char = *psz_unicode++;
1219 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1224 if( i_char == '\n' )
1226 psz_line_start = psz_unicode;
1227 if( !(p_next = NewLine( strlen( psz_string ))) )
1229 msg_Err( p_filter, "out of memory" );
1232 p_line->p_next = p_next;
1233 p_line->i_width = line.xMax;
1234 p_line->i_height = face->size->metrics.height >> 6;
1235 p_line->pp_glyphs[ i ] = NULL;
1236 p_line->i_alpha = i_font_alpha;
1237 p_line->i_red = i_red;
1238 p_line->i_green = i_green;
1239 p_line->i_blue = i_blue;
1242 result.x = __MAX( result.x, line.xMax );
1243 result.y += face->size->metrics.height >> 6;
1246 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1247 i_pen_y += face->size->metrics.height >> 6;
1249 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1254 i_glyph_index = FT_Get_Char_Index( face, i_char );
1255 if( p_sys->i_use_kerning && i_glyph_index
1259 FT_Get_Kerning( face, i_previous, i_glyph_index,
1260 ft_kerning_default, &delta );
1261 i_pen_x += delta.x >> 6;
1264 p_line->p_glyph_pos[ i ].x = i_pen_x;
1265 p_line->p_glyph_pos[ i ].y = i_pen_y;
1266 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1269 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1273 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1276 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1280 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1281 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1284 FT_Done_Glyph( tmp_glyph );
1287 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1290 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1291 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1292 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
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 if( psz_unicode_orig ) free( psz_unicode_orig );
1360 FreeLines( p_lines );
1364 if( psz_unicode_orig ) 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, vlc_bool_t b_bold,
1372 vlc_bool_t b_italic, vlc_bool_t 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 if( p_style->psz_fontname )
1395 free( p_style->psz_fontname );
1400 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1407 if(( s1->i_font_size == s2->i_font_size ) &&
1408 ( s1->i_font_color == s2->i_font_color ) &&
1409 ( s1->b_italic == s2->b_italic ) &&
1410 ( s1->b_bold == s2->b_bold ) &&
1411 ( s1->b_underline == s2->b_underline ) &&
1412 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1419 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1420 uint32_t i_color, uint32_t i_karaoke_bg_color )
1422 font_stack_t *p_new;
1425 return VLC_EGENERIC;
1427 p_new = malloc( sizeof( font_stack_t ) );
1431 p_new->p_next = NULL;
1434 p_new->psz_name = strdup( psz_name );
1436 p_new->psz_name = NULL;
1438 p_new->i_size = i_size;
1439 p_new->i_color = i_color;
1440 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1448 font_stack_t *p_last;
1450 for( p_last = *p_font;
1452 p_last = p_last->p_next )
1455 p_last->p_next = p_new;
1460 static int PopFont( font_stack_t **p_font )
1462 font_stack_t *p_last, *p_next_to_last;
1464 if( !p_font || !*p_font )
1465 return VLC_EGENERIC;
1467 p_next_to_last = NULL;
1468 for( p_last = *p_font;
1470 p_last = p_last->p_next )
1472 p_next_to_last = p_last;
1475 if( p_next_to_last )
1476 p_next_to_last->p_next = NULL;
1480 free( p_last->psz_name );
1486 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1487 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1489 font_stack_t *p_last;
1491 if( !p_font || !*p_font )
1492 return VLC_EGENERIC;
1494 for( p_last=*p_font;
1496 p_last=p_last->p_next )
1499 *psz_name = p_last->psz_name;
1500 *i_size = p_last->i_size;
1501 *i_color = p_last->i_color;
1502 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1507 static void IconvText( filter_t *p_filter, const char *psz_string,
1508 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1510 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1512 /* If memory hasn't been allocated for our output string, allocate it here
1513 * - the calling function must now be responsible for freeing it.
1515 if( !*ppsz_unicode )
1516 *ppsz_unicode = (uint32_t *)
1517 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1519 /* We don't need to handle a NULL pointer in *ppsz_unicode
1520 * if we are instead testing for a non NULL value like we are here */
1524 #if defined(WORDS_BIGENDIAN)
1525 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1527 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1529 if( iconv_handle != (vlc_iconv_t)-1 )
1531 char *p_in_buffer, *p_out_buffer;
1532 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1533 i_in_bytes = strlen( psz_string );
1534 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1535 i_out_bytes_left = i_out_bytes;
1536 p_in_buffer = (char *) psz_string;
1537 p_out_buffer = (char *) *ppsz_unicode;
1538 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1539 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1541 vlc_iconv_close( iconv_handle );
1545 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1546 "bytes left %u", (unsigned)i_in_bytes );
1550 *(uint32_t*)p_out_buffer = 0;
1552 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1557 msg_Warn( p_filter, "unable to do conversion" );
1562 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1563 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1564 vlc_bool_t b_uline )
1566 ft_style_t *p_style = NULL;
1568 char *psz_fontname = NULL;
1569 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1570 uint32_t i_karaoke_bg_color = i_font_color;
1571 int i_font_size = p_sys->i_font_size;
1573 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1574 &i_font_color, &i_karaoke_bg_color ))
1576 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1577 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1582 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1583 vlc_bool_t b_uline, int i_karaoke_bgcolor,
1584 line_desc_t *p_line, uint32_t *psz_unicode,
1585 int *pi_pen_x, int i_pen_y, int *pi_start,
1586 FT_Vector *p_result )
1591 vlc_bool_t b_first_on_line = VLC_TRUE;
1594 int i_pen_x_start = *pi_pen_x;
1596 uint32_t *psz_unicode_start = psz_unicode;
1598 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1600 /* Account for part of line already in position */
1601 for( i=0; i<*pi_start; i++ )
1605 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1606 ft_glyph_bbox_pixels, &glyph_size );
1608 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1609 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1610 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1611 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1617 b_first_on_line = VLC_FALSE;
1619 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1625 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1626 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1630 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1631 ft_kerning_default, &delta );
1632 *pi_pen_x += delta.x >> 6;
1634 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1635 p_line->p_glyph_pos[ i ].y = i_pen_y;
1637 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1641 "unable to render text FT_Load_Glyph returned %d", i_error );
1642 p_line->pp_glyphs[ i ] = NULL;
1643 return VLC_EGENERIC;
1645 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1649 "unable to render text FT_Get_Glyph returned %d", i_error );
1650 p_line->pp_glyphs[ i ] = NULL;
1651 return VLC_EGENERIC;
1653 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1654 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1657 FT_Done_Glyph( tmp_glyph );
1662 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1663 p_face->size->metrics.y_scale));
1664 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1665 p_face->size->metrics.y_scale));
1667 p_line->pi_underline_offset[ i ] =
1668 ( aOffset < 0 ) ? -aOffset : aOffset;
1669 p_line->pi_underline_thickness[ i ] =
1670 ( aSize < 0 ) ? -aSize : aSize;
1672 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1673 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1674 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1675 p_line->p_fg_bg_ratio[ i ] = 0x00;
1677 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1678 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1679 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1681 while( --i > *pi_start )
1683 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1686 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1690 if( psz_unicode == psz_unicode_start )
1692 if( b_first_on_line )
1694 msg_Warn( p_filter, "unbreakable string" );
1695 p_line->pp_glyphs[ i ] = NULL;
1696 return VLC_EGENERIC;
1698 *pi_pen_x = i_pen_x_start;
1700 p_line->i_width = line.xMax;
1701 p_line->i_height = __MAX( p_line->i_height,
1702 p_face->size->metrics.height >> 6 );
1703 p_line->pp_glyphs[ i ] = NULL;
1705 p_result->x = __MAX( p_result->x, line.xMax );
1706 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1707 i_yMax - i_yMin ) );
1714 *psz_unicode = '\n';
1716 psz_unicode = psz_unicode_start;
1717 *pi_pen_x = i_pen_x_start;
1725 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1726 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1728 i_previous = i_glyph_index;
1729 *pi_pen_x += p_face->glyph->advance.x >> 6;
1732 p_line->i_width = line.xMax;
1733 p_line->i_height = __MAX( p_line->i_height,
1734 p_face->size->metrics.height >> 6 );
1735 p_line->pp_glyphs[ i ] = NULL;
1737 p_result->x = __MAX( p_result->x, line.xMax );
1738 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1739 line.yMax - line.yMin ) );
1743 /* Get rid of any text processed - if necessary repositioning
1744 * at the start of a new line of text
1748 *psz_unicode_start = '\0';
1750 else if( psz_unicode > psz_unicode_start )
1752 for( i=0; psz_unicode[ i ]; i++ )
1753 psz_unicode_start[ i ] = psz_unicode[ i ];
1754 psz_unicode_start[ i ] = '\0';
1760 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1761 font_stack_t **p_fonts, int i_scale )
1764 char *psz_fontname = NULL;
1765 uint32_t i_font_color = 0xffffff;
1766 int i_font_alpha = 0;
1767 uint32_t i_karaoke_bg_color = 0x00ffffff;
1768 int i_font_size = 24;
1770 /* Default all attributes to the top font in the stack -- in case not
1771 * all attributes are specified in the sub-font
1773 if( VLC_SUCCESS == PeekFont( p_fonts,
1777 &i_karaoke_bg_color ))
1779 psz_fontname = strdup( psz_fontname );
1780 i_font_size = i_font_size * 1000 / i_scale;
1782 i_font_alpha = (i_font_color >> 24) & 0xff;
1783 i_font_color &= 0x00ffffff;
1785 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1787 char *psz_name = xml_ReaderName( p_xml_reader );
1788 char *psz_value = xml_ReaderValue( p_xml_reader );
1790 if( psz_name && psz_value )
1792 if( !strcasecmp( "face", psz_name ) )
1794 if( psz_fontname ) free( psz_fontname );
1795 psz_fontname = strdup( psz_value );
1797 else if( !strcasecmp( "size", psz_name ) )
1799 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1801 int i_value = atoi( psz_value );
1803 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1804 i_font_size += ( i_value * i_font_size ) / 10;
1805 else if( i_value < -5 )
1806 i_font_size = - i_value;
1807 else if( i_value > 5 )
1808 i_font_size = i_value;
1811 i_font_size = atoi( psz_value );
1813 else if( !strcasecmp( "color", psz_name ) &&
1814 ( psz_value[0] == '#' ) )
1816 i_font_color = strtol( psz_value + 1, NULL, 16 );
1817 i_font_color &= 0x00ffffff;
1819 else if( !strcasecmp( "alpha", psz_name ) &&
1820 ( psz_value[0] == '#' ) )
1822 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1823 i_font_alpha &= 0xff;
1829 rv = PushFont( p_fonts,
1831 i_font_size * i_scale / 1000,
1832 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1833 i_karaoke_bg_color );
1835 free( psz_fontname );
1840 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1841 uint32_t **psz_text_out, uint32_t *pi_runs,
1842 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1843 ft_style_t *p_style )
1845 uint32_t i_string_length = 0;
1847 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1848 *psz_text_out += i_string_length;
1850 if( ppp_styles && ppi_run_lengths )
1856 *ppp_styles = (ft_style_t **)
1857 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1859 else if( *pi_runs == 1 )
1861 *ppp_styles = (ft_style_t **)
1862 malloc( *pi_runs * sizeof( ft_style_t * ) );
1865 /* We have just malloc'ed this memory successfully -
1866 * *pi_runs HAS to be within the memory area of *ppp_styles */
1869 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1873 if( *ppi_run_lengths )
1875 *ppi_run_lengths = (uint32_t *)
1876 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1878 else if( *pi_runs == 1 )
1880 *ppi_run_lengths = (uint32_t *)
1881 malloc( *pi_runs * sizeof( uint32_t ) );
1884 /* same remarks here */
1885 if( *ppi_run_lengths )
1887 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1890 /* If we couldn't use the p_style argument due to memory allocation
1891 * problems above, release it here.
1893 if( p_style ) DeleteStyle( p_style );
1896 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1897 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1899 /* Karaoke tags _PRECEDE_ the text they specify a duration
1900 * for, therefore we are working out the length for the
1901 * previous tag, and first time through we have nothing
1903 if( pi_k_run_lengths )
1908 /* Work out how many characters are presently in the string
1910 for( i = 0; i < i_runs; i++ )
1911 i_chars += pi_run_lengths[ i ];
1913 /* Subtract away those we've already allocated to other
1916 for( i = 0; i < i_k_runs; i++ )
1917 i_chars -= pi_k_run_lengths[ i ];
1919 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1923 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1924 uint32_t **ppi_k_run_lengths,
1925 uint32_t **ppi_k_durations )
1927 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1929 char *psz_name = xml_ReaderName( p_xml_reader );
1930 char *psz_value = xml_ReaderValue( p_xml_reader );
1932 if( psz_name && psz_value &&
1933 !strcasecmp( "t", psz_name ) )
1935 if( ppi_k_durations && ppi_k_run_lengths )
1939 if( *ppi_k_durations )
1941 *ppi_k_durations = (uint32_t *)
1942 realloc( *ppi_k_durations,
1943 *pi_k_runs * sizeof( uint32_t ) );
1945 else if( *pi_k_runs == 1 )
1947 *ppi_k_durations = (uint32_t *)
1948 malloc( *pi_k_runs * sizeof( uint32_t ) );
1951 if( *ppi_k_run_lengths )
1953 *ppi_k_run_lengths = (uint32_t *)
1954 realloc( *ppi_k_run_lengths,
1955 *pi_k_runs * sizeof( uint32_t ) );
1957 else if( *pi_k_runs == 1 )
1959 *ppi_k_run_lengths = (uint32_t *)
1960 malloc( *pi_k_runs * sizeof( uint32_t ) );
1962 if( *ppi_k_durations )
1963 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1965 if( *ppi_k_run_lengths )
1966 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1969 if( psz_name ) free( psz_name );
1970 if( psz_value ) free( psz_value );
1974 static int ProcessNodes( filter_t *p_filter,
1975 xml_reader_t *p_xml_reader,
1976 text_style_t *p_font_style,
1981 uint32_t **ppi_run_lengths,
1982 ft_style_t ***ppp_styles,
1984 vlc_bool_t b_karaoke,
1985 uint32_t *pi_k_runs,
1986 uint32_t **ppi_k_run_lengths,
1987 uint32_t **ppi_k_durations )
1989 int rv = VLC_SUCCESS;
1990 filter_sys_t *p_sys = p_filter->p_sys;
1991 uint32_t *psz_text_orig = psz_text;
1992 font_stack_t *p_fonts = NULL;
1996 char *psz_node = NULL;
1998 vlc_bool_t b_italic = VLC_FALSE;
1999 vlc_bool_t b_bold = VLC_FALSE;
2000 vlc_bool_t b_uline = VLC_FALSE;
2002 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2003 i_scale = val.i_int;
2007 rv = PushFont( &p_fonts,
2008 p_font_style->psz_fontname,
2009 p_font_style->i_font_size * i_scale / 1000,
2010 (p_font_style->i_font_color & 0xffffff) |
2011 ((p_font_style->i_font_alpha & 0xff) << 24),
2012 (p_font_style->i_karaoke_background_color & 0xffffff) |
2013 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2015 if( p_font_style->i_style_flags & STYLE_BOLD )
2017 if( p_font_style->i_style_flags & STYLE_ITALIC )
2018 b_italic = VLC_TRUE;
2019 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2024 rv = PushFont( &p_fonts,
2030 if( rv != VLC_SUCCESS )
2033 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2035 switch ( xml_ReaderNodeType( p_xml_reader ) )
2037 case XML_READER_NONE:
2039 case XML_READER_ENDELEM:
2040 psz_node = xml_ReaderName( p_xml_reader );
2044 if( !strcasecmp( "font", psz_node ) )
2045 PopFont( &p_fonts );
2046 else if( !strcasecmp( "b", psz_node ) )
2048 else if( !strcasecmp( "i", psz_node ) )
2049 b_italic = VLC_FALSE;
2050 else if( !strcasecmp( "u", psz_node ) )
2051 b_uline = VLC_FALSE;
2056 case XML_READER_STARTELEM:
2057 psz_node = xml_ReaderName( p_xml_reader );
2060 if( !strcasecmp( "font", psz_node ) )
2061 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2062 else if( !strcasecmp( "b", psz_node ) )
2064 else if( !strcasecmp( "i", psz_node ) )
2065 b_italic = VLC_TRUE;
2066 else if( !strcasecmp( "u", psz_node ) )
2068 else if( !strcasecmp( "br", psz_node ) )
2070 SetupLine( p_filter, "\n", &psz_text,
2071 pi_runs, ppi_run_lengths, ppp_styles,
2072 GetStyleFromFontStack( p_sys,
2078 else if( !strcasecmp( "k", psz_node ) )
2080 /* Only valid in karaoke */
2083 if( *pi_k_runs > 0 )
2085 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2086 *pi_k_runs, *ppi_k_run_lengths );
2088 SetupKaraoke( p_xml_reader, pi_k_runs,
2089 ppi_k_run_lengths, ppi_k_durations );
2096 case XML_READER_TEXT:
2097 psz_node = xml_ReaderValue( p_xml_reader );
2100 /* Turn any multiple-whitespaces into single spaces */
2101 char *s = strpbrk( psz_node, "\t\r\n " );
2104 int i_whitespace = strspn( s, "\t\r\n " );
2106 if( i_whitespace > 1 )
2109 strlen( s ) - i_whitespace + 1 );
2112 s = strpbrk( s, "\t\r\n " );
2114 SetupLine( p_filter, psz_node, &psz_text,
2115 pi_runs, ppi_run_lengths, ppp_styles,
2116 GetStyleFromFontStack( p_sys,
2125 if( rv != VLC_SUCCESS )
2127 psz_text = psz_text_orig;
2133 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2134 *pi_k_runs, *ppi_k_run_lengths );
2137 *pi_len = psz_text - psz_text_orig;
2139 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2144 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2148 for( k=0; k < p_sys->i_font_attachments; k++ )
2150 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2152 FT_Face p_face = NULL;
2154 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2162 vlc_bool_t match = !strcasecmp( p_face->family_name,
2163 p_style->psz_fontname );
2165 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2166 match = match && p_style->b_bold;
2168 match = match && !p_style->b_bold;
2170 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2171 match = match && p_style->b_italic;
2173 match = match && !p_style->b_italic;
2181 FT_Done_Face( p_face );
2186 return VLC_EGENERIC;
2189 static int CheckIfFontBuildComplete( filter_t *p_filter )
2191 filter_sys_t *p_sys = p_filter->p_sys;
2192 vlc_object_t *p_fb = vlc_object_find_name( p_filter->p_libvlc,
2197 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
2200 if( VLC_SUCCESS == var_Get( p_fb, "build-done", &val ))
2202 p_sys->b_fontconfig_ok = val.b_bool;
2204 if( p_sys->b_fontconfig_ok )
2207 p_sys->p_fontconfig = FcConfigGetCurrent();
2210 msg_Dbg( p_filter, "Font Build still not complete" );
2212 vlc_mutex_unlock( lock );
2213 vlc_object_release( p_fb );
2217 return VLC_EGENERIC;
2220 static int ProcessLines( filter_t *p_filter,
2225 uint32_t *pi_run_lengths,
2226 ft_style_t **pp_styles,
2227 line_desc_t **pp_lines,
2229 FT_Vector *p_result,
2231 vlc_bool_t b_karaoke,
2233 uint32_t *pi_k_run_lengths,
2234 uint32_t *pi_k_durations )
2236 filter_sys_t *p_sys = p_filter->p_sys;
2237 ft_style_t **pp_char_styles;
2238 int *p_new_positions = NULL;
2239 int8_t *p_levels = NULL;
2240 uint8_t *pi_karaoke_bar = NULL;
2244 /* Assign each character in the text string its style explicitly, so that
2245 * after the characters have been shuffled around by Fribidi, we can re-apply
2246 * the styles, and to simplify the calculation of runs within a line.
2248 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2249 if( !pp_char_styles )
2254 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2255 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2256 * we just won't be able to display the progress bar; at least we'll
2262 for( j = 0; j < i_runs; j++ )
2263 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2264 pp_char_styles[ i++ ] = pp_styles[ j ];
2266 #if defined(HAVE_FRIBIDI)
2268 ft_style_t **pp_char_styles_new;
2269 int *p_old_positions;
2270 uint32_t *p_fribidi_string;
2271 int start_pos, pos = 0;
2273 pp_char_styles_new = (ft_style_t **)
2274 malloc( i_len * sizeof( ft_style_t * ));
2276 p_fribidi_string = (uint32_t *)
2277 malloc( (i_len + 1) * sizeof(uint32_t) );
2278 p_old_positions = (int *)
2279 malloc( (i_len + 1) * sizeof( int ) );
2280 p_new_positions = (int *)
2281 malloc( (i_len + 1) * sizeof( int ) );
2282 p_levels = (int8_t *)
2283 malloc( (i_len + 1) * sizeof( int8_t ) );
2285 if( ! pp_char_styles_new ||
2286 ! p_fribidi_string ||
2287 ! p_old_positions ||
2288 ! p_new_positions ||
2291 msg_Err( p_filter, "out of memory" );
2292 if( p_levels ) free( p_levels );
2293 if( p_old_positions ) free( p_old_positions );
2294 if( p_new_positions ) free( p_new_positions );
2295 if( p_fribidi_string ) free( p_fribidi_string );
2296 if( pp_char_styles_new ) free( pp_char_styles_new );
2297 if( pi_karaoke_bar ) free( pi_karaoke_bar );
2299 free( pp_char_styles );
2303 /* Do bidi conversion line-by-line */
2306 while(pos < i_len) {
2307 if (psz_text[pos] != '\n')
2309 p_fribidi_string[pos] = psz_text[pos];
2310 pp_char_styles_new[pos] = pp_char_styles[pos];
2311 p_new_positions[pos] = pos;
2316 while(pos < i_len) {
2317 if (psz_text[pos] == '\n')
2321 if (pos > start_pos)
2323 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2324 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2325 pos - start_pos, &base_dir,
2326 (FriBidiChar*)p_fribidi_string + start_pos,
2327 p_new_positions + start_pos,
2329 p_levels + start_pos );
2330 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2332 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2333 p_old_positions[ j - start_pos ] ];
2334 p_new_positions[ j ] += start_pos;
2338 free( p_old_positions );
2339 free( pp_char_styles );
2340 pp_char_styles = pp_char_styles_new;
2341 psz_text = p_fribidi_string;
2342 p_fribidi_string[ i_len ] = 0;
2345 /* Work out the karaoke */
2346 if( pi_karaoke_bar )
2348 int64_t i_last_duration = 0;
2349 int64_t i_duration = 0;
2350 int64_t i_start_pos = 0;
2351 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2353 for( k = 0; k< i_k_runs; k++ )
2355 double fraction = 0.0;
2357 i_duration += pi_k_durations[ k ];
2359 if( i_duration < i_elapsed )
2361 /* Completely finished this run-length -
2362 * let it render normally */
2366 else if( i_elapsed < i_last_duration )
2368 /* Haven't got up to this segment yet -
2369 * render it completely in karaoke BG mode */
2375 /* Partway through this run */
2377 fraction = (double)(i_elapsed - i_last_duration) /
2378 (double)pi_k_durations[ k ];
2380 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2382 double shade = pi_k_run_lengths[ k ] * fraction;
2384 if( p_new_positions )
2385 j = p_new_positions[ i_start_pos + i ];
2387 j = i_start_pos + i;
2389 if( i < (uint32_t)shade )
2390 pi_karaoke_bar[ j ] = 0xff;
2391 else if( (double)i > shade )
2392 pi_karaoke_bar[ j ] = 0x00;
2395 shade -= (int)shade;
2396 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2397 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2401 i_last_duration = i_duration;
2402 i_start_pos += pi_k_run_lengths[ k ];
2405 if( p_levels ) free( p_levels );
2406 if( p_new_positions ) free( p_new_positions );
2408 FT_Vector tmp_result;
2410 line_desc_t *p_line = NULL;
2411 line_desc_t *p_prev = NULL;
2417 p_result->x = p_result->y = 0;
2418 tmp_result.x = tmp_result.y = 0;
2421 for( k = 0; k <= (uint32_t) i_len; k++ )
2423 if( ( k == (uint32_t) i_len ) ||
2425 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2427 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2429 /* End of the current style run */
2430 FT_Face p_face = NULL;
2433 /* Look for a match amongst our attachments first */
2434 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2436 if( !p_sys->b_fontconfig_ok )
2438 if( VLC_EGENERIC == CheckIfFontBuildComplete( p_filter ))
2439 msg_Err( p_filter, "Can't find FontBuilder thread!" );
2442 if( ! p_face && p_sys->b_fontconfig_ok )
2445 vlc_mutex_lock( &p_sys->fontconfig_lock );
2447 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2448 p_style->psz_fontname,
2452 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2454 if( psz_fontfile && ! *psz_fontfile )
2456 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2457 " so using default font", p_style->psz_fontname,
2458 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2459 (p_style->b_bold ? "(Bold)" :
2460 (p_style->b_italic ? "(Italic)" : ""))) );
2461 free( psz_fontfile );
2462 psz_fontfile = NULL;
2467 if( FT_New_Face( p_sys->p_library,
2468 psz_fontfile, i_idx, &p_face ) )
2470 free( psz_fontfile );
2471 free( pp_char_styles );
2472 #if defined(HAVE_FRIBIDI)
2475 if( pi_karaoke_bar )
2476 free( pi_karaoke_bar );
2477 return VLC_EGENERIC;
2479 free( psz_fontfile );
2483 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2485 /* We've loaded a font face which is unhelpful for actually
2486 * rendering text - fallback to the default one.
2488 FT_Done_Face( p_face );
2492 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2493 ft_encoding_unicode ) ||
2494 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2495 p_style->i_font_size ) )
2497 if( p_face ) FT_Done_Face( p_face );
2498 free( pp_char_styles );
2499 #if defined(HAVE_FRIBIDI)
2502 if( pi_karaoke_bar )
2503 free( pi_karaoke_bar );
2504 return VLC_EGENERIC;
2506 p_sys->i_use_kerning =
2507 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2510 uint32_t *psz_unicode = (uint32_t *)
2511 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2514 msg_Err( p_filter, "out of memory" );
2515 if( p_face ) FT_Done_Face( p_face );
2516 free( pp_char_styles );
2517 free( psz_unicode );
2518 #if defined(HAVE_FRIBIDI)
2521 if( pi_karaoke_bar )
2522 free( pi_karaoke_bar );
2525 memcpy( psz_unicode, psz_text + i_prev,
2526 sizeof( uint32_t ) * ( k - i_prev ) );
2527 psz_unicode[ k - i_prev ] = 0;
2528 while( *psz_unicode )
2532 if( !(p_line = NewLine( i_len - i_prev)) )
2534 msg_Err( p_filter, "out of memory" );
2535 if( p_face ) FT_Done_Face( p_face );
2536 free( pp_char_styles );
2537 free( psz_unicode );
2538 #if defined(HAVE_FRIBIDI)
2541 if( pi_karaoke_bar )
2542 free( pi_karaoke_bar );
2545 /* New Color mode only works in YUVA rendering mode --
2546 * (RGB mode has palette constraints on it). We therefore
2547 * need to populate the legacy colour fields also.
2549 p_line->b_new_color_mode = VLC_TRUE;
2550 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2551 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2552 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2553 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2554 p_line->p_next = NULL;
2556 i_pen_y += tmp_result.y;
2560 if( p_prev ) p_prev->p_next = p_line;
2561 else *pp_lines = p_line;
2564 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2565 p_style->i_font_color, p_style->b_underline,
2566 p_style->i_karaoke_bg_color,
2567 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2568 &tmp_result ) != VLC_SUCCESS )
2570 if( p_face ) FT_Done_Face( p_face );
2571 free( pp_char_styles );
2572 free( psz_unicode );
2573 #if defined(HAVE_FRIBIDI)
2576 if( pi_karaoke_bar )
2577 free( pi_karaoke_bar );
2578 return VLC_EGENERIC;
2583 p_result->x = __MAX( p_result->x, tmp_result.x );
2584 p_result->y += tmp_result.y;
2589 if( *psz_unicode == '\n')
2593 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2595 *c_ptr = *(c_ptr+1);
2600 free( psz_unicode );
2601 if( p_face ) FT_Done_Face( p_face );
2605 free( pp_char_styles );
2606 #if defined(HAVE_FRIBIDI)
2611 p_result->x = __MAX( p_result->x, tmp_result.x );
2612 p_result->y += tmp_result.y;
2615 if( pi_karaoke_bar )
2618 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2620 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2622 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2626 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2628 /* 100% BG colour will render faster if we
2629 * instead make it 100% FG colour, so leave
2630 * the ratio alone and copy the value across
2632 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2636 if( pi_karaoke_bar[ i ] & 0x80 )
2638 /* Swap Left and Right sides over for Right aligned
2639 * language text (eg. Arabic, Hebrew)
2641 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2643 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2644 p_line->p_bg_rgb[ k ] = i_tmp;
2646 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2649 /* Jump over the '\n' at the line-end */
2652 free( pi_karaoke_bar );
2658 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2659 subpicture_region_t *p_region_in )
2661 int rv = VLC_SUCCESS;
2662 stream_t *p_sub = NULL;
2663 xml_t *p_xml = NULL;
2664 xml_reader_t *p_xml_reader = NULL;
2666 if( !p_region_in || !p_region_in->psz_html )
2667 return VLC_EGENERIC;
2669 /* Reset the default fontsize in case screen metrics have changed */
2670 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2672 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2673 (uint8_t *) p_region_in->psz_html,
2674 strlen( p_region_in->psz_html ),
2678 p_xml = xml_Create( p_filter );
2681 vlc_bool_t b_karaoke = VLC_FALSE;
2683 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2686 /* Look for Root Node */
2687 if( xml_ReaderRead( p_xml_reader ) == 1 )
2689 char *psz_node = xml_ReaderName( p_xml_reader );
2691 if( !strcasecmp( "karaoke", psz_node ) )
2693 /* We're going to have to render the text a number
2694 * of times to show the progress marker on the text.
2696 var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2697 b_karaoke = VLC_TRUE;
2699 else if( !strcasecmp( "text", psz_node ) )
2701 b_karaoke = VLC_FALSE;
2705 /* Only text and karaoke tags are supported */
2706 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2707 xml_ReaderDelete( p_xml, p_xml_reader );
2708 p_xml_reader = NULL;
2720 uint32_t i_runs = 0;
2721 uint32_t i_k_runs = 0;
2722 uint32_t *pi_run_lengths = NULL;
2723 uint32_t *pi_k_run_lengths = NULL;
2724 uint32_t *pi_k_durations = NULL;
2725 ft_style_t **pp_styles = NULL;
2727 line_desc_t *p_lines = NULL;
2729 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2730 sizeof( uint32_t ) );
2735 rv = ProcessNodes( p_filter, p_xml_reader,
2736 p_region_in->p_style, psz_text, &i_len,
2737 &i_runs, &pi_run_lengths, &pp_styles,
2738 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2741 p_region_out->i_x = p_region_in->i_x;
2742 p_region_out->i_y = p_region_in->i_y;
2744 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2746 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2747 pi_run_lengths, pp_styles, &p_lines, &result,
2748 b_karaoke, i_k_runs, pi_k_run_lengths,
2752 for( k=0; k<i_runs; k++)
2753 DeleteStyle( pp_styles[k] );
2755 free( pi_run_lengths );
2758 /* Don't attempt to render text that couldn't be layed out
2761 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2763 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2765 Render( p_filter, p_region_out, p_lines,
2766 result.x, result.y );
2770 RenderYUVA( p_filter, p_region_out, p_lines,
2771 result.x, result.y );
2775 FreeLines( p_lines );
2777 xml_ReaderDelete( p_xml, p_xml_reader );
2779 xml_Delete( p_xml );
2781 stream_Delete( p_sub );
2787 static char* FontConfig_Select( FcConfig* priv, const char* family,
2788 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2791 FcPattern *pat, *p_pat;
2795 pat = FcPatternCreate();
2796 if (!pat) return NULL;
2798 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2799 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2800 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2801 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2803 FcDefaultSubstitute( pat );
2805 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2807 FcPatternDestroy( pat );
2811 p_pat = FcFontMatch( priv, pat, &result );
2812 FcPatternDestroy( pat );
2813 if( !p_pat ) return NULL;
2815 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2816 || ( val_b != FcTrue ) )
2818 FcPatternDestroy( p_pat );
2821 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2826 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2828 FcPatternDestroy( p_pat );
2833 if( strcasecmp((const char*)val_s, family ) != 0 )
2834 msg_Warn( p_filter, "fontconfig: selected font family is not"
2835 "the requested one: '%s' != '%s'\n",
2836 (const char*)val_s, family );
2839 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2841 FcPatternDestroy( p_pat );
2845 FcPatternDestroy( p_pat );
2846 return strdup( (const char*)val_s );
2850 static void FreeLine( line_desc_t *p_line )
2853 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2855 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2857 free( p_line->pp_glyphs );
2858 free( p_line->p_glyph_pos );
2859 free( p_line->p_fg_rgb );
2860 free( p_line->p_bg_rgb );
2861 free( p_line->p_fg_bg_ratio );
2862 free( p_line->pi_underline_offset );
2863 free( p_line->pi_underline_thickness );
2867 static void FreeLines( line_desc_t *p_lines )
2869 line_desc_t *p_line, *p_next;
2871 if( !p_lines ) return;
2873 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2875 p_next = p_line->p_next;
2880 static line_desc_t *NewLine( int i_count )
2882 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2884 if( !p_line ) return NULL;
2885 p_line->i_height = 0;
2886 p_line->i_width = 0;
2887 p_line->p_next = NULL;
2889 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2890 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2891 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2892 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2893 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2894 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2895 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2896 if( ( p_line->pp_glyphs == NULL ) ||
2897 ( p_line->p_glyph_pos == NULL ) ||
2898 ( p_line->p_fg_rgb == NULL ) ||
2899 ( p_line->p_bg_rgb == NULL ) ||
2900 ( p_line->p_fg_bg_ratio == NULL ) ||
2901 ( p_line->pi_underline_offset == NULL ) ||
2902 ( p_line->pi_underline_thickness == NULL ) )
2904 if( p_line->pi_underline_thickness )
2905 free( p_line->pi_underline_thickness );
2906 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2907 if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2908 if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2909 if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2910 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2911 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2915 p_line->pp_glyphs[0] = NULL;
2916 p_line->b_new_color_mode = VLC_FALSE;
2921 static int GetFontSize( filter_t *p_filter )
2923 filter_sys_t *p_sys = p_filter->p_sys;
2927 if( p_sys->i_default_font_size )
2929 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2930 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2932 i_size = p_sys->i_default_font_size;
2936 var_Get( p_filter, "freetype-rel-fontsize", &val );
2937 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2938 p_filter->p_sys->i_display_height =
2939 p_filter->fmt_out.video.i_height;
2943 msg_Warn( p_filter, "invalid fontsize, using 12" );
2944 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2945 i_size = 12 * val.i_int / 1000;
2952 static int SetFontSize( filter_t *p_filter, int i_size )
2954 filter_sys_t *p_sys = p_filter->p_sys;
2958 i_size = GetFontSize( p_filter );
2960 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2963 p_sys->i_font_size = i_size;
2965 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2967 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2968 return VLC_EGENERIC;
2974 static void YUVFromRGB( uint32_t i_argb,
2975 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2977 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2978 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2979 int i_blue = ( i_argb & 0x000000ff );
2981 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2982 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2983 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2984 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2985 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2986 -585 * i_blue + 4096 + 1048576) >> 13, 240);