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, &i_in_bytes,
1138 &p_out_buffer, &i_out_bytes_left );
1140 vlc_iconv_close( iconv_handle );
1144 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1145 "bytes left %u", (unsigned)i_in_bytes );
1148 *(uint32_t*)p_out_buffer = 0;
1149 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1152 #if defined(HAVE_FRIBIDI)
1154 uint32_t *p_fribidi_string;
1155 int start_pos, pos = 0;
1157 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1158 if( !p_fribidi_string )
1160 msg_Err( p_filter, "out of memory" );
1164 /* Do bidi conversion line-by-line */
1165 while(pos < i_string_length)
1167 while(pos < i_string_length) {
1168 i_char = psz_unicode[pos];
1169 if (i_char != '\r' && i_char != '\n')
1171 p_fribidi_string[pos] = i_char;
1175 while(pos < i_string_length) {
1176 i_char = psz_unicode[pos];
1177 if (i_char == '\r' || i_char == '\n')
1181 if (pos > start_pos)
1183 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1184 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
1185 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
1189 free( psz_unicode_orig );
1190 psz_unicode = psz_unicode_orig = p_fribidi_string;
1191 p_fribidi_string[ i_string_length ] = 0;
1195 /* Calculate relative glyph positions and a bounding box for the
1197 if( !(p_line = NewLine( strlen( psz_string ))) )
1199 msg_Err( p_filter, "out of memory" );
1203 i_pen_x = i_pen_y = 0;
1205 psz_line_start = psz_unicode;
1207 #define face p_sys->p_face
1208 #define glyph face->glyph
1210 while( *psz_unicode )
1212 i_char = *psz_unicode++;
1213 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1218 if( i_char == '\n' )
1220 psz_line_start = psz_unicode;
1221 if( !(p_next = NewLine( strlen( psz_string ))) )
1223 msg_Err( p_filter, "out of memory" );
1226 p_line->p_next = p_next;
1227 p_line->i_width = line.xMax;
1228 p_line->i_height = face->size->metrics.height >> 6;
1229 p_line->pp_glyphs[ i ] = NULL;
1230 p_line->i_alpha = i_font_alpha;
1231 p_line->i_red = i_red;
1232 p_line->i_green = i_green;
1233 p_line->i_blue = i_blue;
1236 result.x = __MAX( result.x, line.xMax );
1237 result.y += face->size->metrics.height >> 6;
1240 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1241 i_pen_y += face->size->metrics.height >> 6;
1243 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1248 i_glyph_index = FT_Get_Char_Index( face, i_char );
1249 if( p_sys->i_use_kerning && i_glyph_index
1253 FT_Get_Kerning( face, i_previous, i_glyph_index,
1254 ft_kerning_default, &delta );
1255 i_pen_x += delta.x >> 6;
1258 p_line->p_glyph_pos[ i ].x = i_pen_x;
1259 p_line->p_glyph_pos[ i ].y = i_pen_y;
1260 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1263 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1267 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1270 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1274 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1275 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1278 FT_Done_Glyph( tmp_glyph );
1281 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1284 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1285 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1286 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1288 p_line->pp_glyphs[ i ] = NULL;
1290 p_line = NewLine( strlen( psz_string ));
1291 if( p_prev ) p_prev->p_next = p_line;
1292 else p_lines = p_line;
1294 uint32_t *psz_unicode_saved = psz_unicode;
1295 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1299 if( psz_unicode == psz_line_start )
1300 { /* try harder to break that line */
1301 psz_unicode = psz_unicode_saved;
1302 while( psz_unicode > psz_line_start &&
1303 *psz_unicode != '_' && *psz_unicode != '/' &&
1304 *psz_unicode != '\\' && *psz_unicode != '.' )
1309 if( psz_unicode == psz_line_start )
1311 msg_Warn( p_filter, "unbreakable string" );
1316 *psz_unicode = '\n';
1318 psz_unicode = psz_line_start;
1321 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1324 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1325 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1327 i_previous = i_glyph_index;
1328 i_pen_x += glyph->advance.x >> 6;
1332 p_line->i_width = line.xMax;
1333 p_line->i_height = face->size->metrics.height >> 6;
1334 p_line->pp_glyphs[ i ] = NULL;
1335 p_line->i_alpha = i_font_alpha;
1336 p_line->i_red = i_red;
1337 p_line->i_green = i_green;
1338 p_line->i_blue = i_blue;
1339 result.x = __MAX( result.x, line.xMax );
1340 result.y += line.yMax - line.yMin;
1345 p_region_out->i_x = p_region_in->i_x;
1346 p_region_out->i_y = p_region_in->i_y;
1348 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1349 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1351 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1353 if( psz_unicode_orig ) free( psz_unicode_orig );
1354 FreeLines( p_lines );
1358 if( psz_unicode_orig ) free( psz_unicode_orig );
1359 FreeLines( p_lines );
1360 return VLC_EGENERIC;
1363 #ifdef HAVE_FONTCONFIG
1364 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1365 uint32_t i_font_color, uint32_t i_karaoke_bg_color, vlc_bool_t b_bold,
1366 vlc_bool_t b_italic, vlc_bool_t b_uline )
1368 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1372 p_style->i_font_size = i_font_size;
1373 p_style->i_font_color = i_font_color;
1374 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1375 p_style->b_italic = b_italic;
1376 p_style->b_bold = b_bold;
1377 p_style->b_underline = b_uline;
1379 p_style->psz_fontname = strdup( psz_fontname );
1384 static void DeleteStyle( ft_style_t *p_style )
1388 if( p_style->psz_fontname )
1389 free( p_style->psz_fontname );
1394 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1401 if(( s1->i_font_size == s2->i_font_size ) &&
1402 ( s1->i_font_color == s2->i_font_color ) &&
1403 ( s1->b_italic == s2->b_italic ) &&
1404 ( s1->b_bold == s2->b_bold ) &&
1405 ( s1->b_underline == s2->b_underline ) &&
1406 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1413 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1414 uint32_t i_color, uint32_t i_karaoke_bg_color )
1416 font_stack_t *p_new;
1419 return VLC_EGENERIC;
1421 p_new = malloc( sizeof( font_stack_t ) );
1425 p_new->p_next = NULL;
1428 p_new->psz_name = strdup( psz_name );
1430 p_new->psz_name = NULL;
1432 p_new->i_size = i_size;
1433 p_new->i_color = i_color;
1434 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1442 font_stack_t *p_last;
1444 for( p_last = *p_font;
1446 p_last = p_last->p_next )
1449 p_last->p_next = p_new;
1454 static int PopFont( font_stack_t **p_font )
1456 font_stack_t *p_last, *p_next_to_last;
1458 if( !p_font || !*p_font )
1459 return VLC_EGENERIC;
1461 p_next_to_last = NULL;
1462 for( p_last = *p_font;
1464 p_last = p_last->p_next )
1466 p_next_to_last = p_last;
1469 if( p_next_to_last )
1470 p_next_to_last->p_next = NULL;
1474 free( p_last->psz_name );
1480 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1481 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1483 font_stack_t *p_last;
1485 if( !p_font || !*p_font )
1486 return VLC_EGENERIC;
1488 for( p_last=*p_font;
1490 p_last=p_last->p_next )
1493 *psz_name = p_last->psz_name;
1494 *i_size = p_last->i_size;
1495 *i_color = p_last->i_color;
1496 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1501 static void IconvText( filter_t *p_filter, const char *psz_string,
1502 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1504 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1506 /* If memory hasn't been allocated for our output string, allocate it here
1507 * - the calling function must now be responsible for freeing it.
1509 if( !*ppsz_unicode )
1510 *ppsz_unicode = (uint32_t *)
1511 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1513 /* We don't need to handle a NULL pointer in *ppsz_unicode
1514 * if we are instead testing for a non NULL value like we are here */
1518 #if defined(WORDS_BIGENDIAN)
1519 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1521 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1523 if( iconv_handle != (vlc_iconv_t)-1 )
1525 char *p_in_buffer, *p_out_buffer;
1526 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1527 i_in_bytes = strlen( psz_string );
1528 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1529 i_out_bytes_left = i_out_bytes;
1530 p_in_buffer = (char *) psz_string;
1531 p_out_buffer = (char *) *ppsz_unicode;
1532 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1533 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1535 vlc_iconv_close( iconv_handle );
1539 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1540 "bytes left %u", (unsigned)i_in_bytes );
1544 *(uint32_t*)p_out_buffer = 0;
1546 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1551 msg_Warn( p_filter, "unable to do conversion" );
1556 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1557 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1558 vlc_bool_t b_uline )
1560 ft_style_t *p_style = NULL;
1562 char *psz_fontname = NULL;
1563 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1564 uint32_t i_karaoke_bg_color = i_font_color;
1565 int i_font_size = p_sys->i_font_size;
1567 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1568 &i_font_color, &i_karaoke_bg_color ))
1570 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1571 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1576 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1577 vlc_bool_t b_uline, int i_karaoke_bgcolor,
1578 line_desc_t *p_line, uint32_t *psz_unicode,
1579 int *pi_pen_x, int i_pen_y, int *pi_start,
1580 FT_Vector *p_result )
1585 vlc_bool_t b_first_on_line = VLC_TRUE;
1588 int i_pen_x_start = *pi_pen_x;
1590 uint32_t *psz_unicode_start = psz_unicode;
1592 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1594 /* Account for part of line already in position */
1595 for( i=0; i<*pi_start; i++ )
1599 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1600 ft_glyph_bbox_pixels, &glyph_size );
1602 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1603 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1604 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1605 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1611 b_first_on_line = VLC_FALSE;
1613 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1619 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1620 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1624 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1625 ft_kerning_default, &delta );
1626 *pi_pen_x += delta.x >> 6;
1628 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1629 p_line->p_glyph_pos[ i ].y = i_pen_y;
1631 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1635 "unable to render text FT_Load_Glyph returned %d", i_error );
1636 p_line->pp_glyphs[ i ] = NULL;
1637 return VLC_EGENERIC;
1639 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1643 "unable to render text FT_Get_Glyph returned %d", i_error );
1644 p_line->pp_glyphs[ i ] = NULL;
1645 return VLC_EGENERIC;
1647 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1648 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1651 FT_Done_Glyph( tmp_glyph );
1656 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1657 p_face->size->metrics.y_scale));
1658 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1659 p_face->size->metrics.y_scale));
1661 p_line->pi_underline_offset[ i ] =
1662 ( aOffset < 0 ) ? -aOffset : aOffset;
1663 p_line->pi_underline_thickness[ i ] =
1664 ( aSize < 0 ) ? -aSize : aSize;
1666 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1667 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1668 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1669 p_line->p_fg_bg_ratio[ i ] = 0x00;
1671 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1672 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1673 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1675 while( --i > *pi_start )
1677 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1680 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1684 if( psz_unicode == psz_unicode_start )
1686 if( b_first_on_line )
1688 msg_Warn( p_filter, "unbreakable string" );
1689 p_line->pp_glyphs[ i ] = NULL;
1690 return VLC_EGENERIC;
1692 *pi_pen_x = i_pen_x_start;
1694 p_line->i_width = line.xMax;
1695 p_line->i_height = __MAX( p_line->i_height,
1696 p_face->size->metrics.height >> 6 );
1697 p_line->pp_glyphs[ i ] = NULL;
1699 p_result->x = __MAX( p_result->x, line.xMax );
1700 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1701 i_yMax - i_yMin ) );
1708 *psz_unicode = '\n';
1710 psz_unicode = psz_unicode_start;
1711 *pi_pen_x = i_pen_x_start;
1719 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1720 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1722 i_previous = i_glyph_index;
1723 *pi_pen_x += p_face->glyph->advance.x >> 6;
1726 p_line->i_width = line.xMax;
1727 p_line->i_height = __MAX( p_line->i_height,
1728 p_face->size->metrics.height >> 6 );
1729 p_line->pp_glyphs[ i ] = NULL;
1731 p_result->x = __MAX( p_result->x, line.xMax );
1732 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1733 line.yMax - line.yMin ) );
1737 /* Get rid of any text processed - if necessary repositioning
1738 * at the start of a new line of text
1742 *psz_unicode_start = '\0';
1744 else if( psz_unicode > psz_unicode_start )
1746 for( i=0; psz_unicode[ i ]; i++ )
1747 psz_unicode_start[ i ] = psz_unicode[ i ];
1748 psz_unicode_start[ i ] = '\0';
1754 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1755 font_stack_t **p_fonts, int i_scale )
1758 char *psz_fontname = NULL;
1759 uint32_t i_font_color = 0xffffff;
1760 int i_font_alpha = 0;
1761 uint32_t i_karaoke_bg_color = 0x00ffffff;
1762 int i_font_size = 24;
1764 /* Default all attributes to the top font in the stack -- in case not
1765 * all attributes are specified in the sub-font
1767 if( VLC_SUCCESS == PeekFont( p_fonts,
1771 &i_karaoke_bg_color ))
1773 psz_fontname = strdup( psz_fontname );
1774 i_font_size = i_font_size * 1000 / i_scale;
1776 i_font_alpha = (i_font_color >> 24) & 0xff;
1777 i_font_color &= 0x00ffffff;
1779 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1781 char *psz_name = xml_ReaderName( p_xml_reader );
1782 char *psz_value = xml_ReaderValue( p_xml_reader );
1784 if( psz_name && psz_value )
1786 if( !strcasecmp( "face", psz_name ) )
1788 if( psz_fontname ) free( psz_fontname );
1789 psz_fontname = strdup( psz_value );
1791 else if( !strcasecmp( "size", psz_name ) )
1793 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1795 int i_value = atoi( psz_value );
1797 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1798 i_font_size += ( i_value * i_font_size ) / 10;
1799 else if( i_value < -5 )
1800 i_font_size = - i_value;
1801 else if( i_value > 5 )
1802 i_font_size = i_value;
1805 i_font_size = atoi( psz_value );
1807 else if( !strcasecmp( "color", psz_name ) &&
1808 ( psz_value[0] == '#' ) )
1810 i_font_color = strtol( psz_value + 1, NULL, 16 );
1811 i_font_color &= 0x00ffffff;
1813 else if( !strcasecmp( "alpha", psz_name ) &&
1814 ( psz_value[0] == '#' ) )
1816 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1817 i_font_alpha &= 0xff;
1823 rv = PushFont( p_fonts,
1825 i_font_size * i_scale / 1000,
1826 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1827 i_karaoke_bg_color );
1829 free( psz_fontname );
1834 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1835 uint32_t **psz_text_out, uint32_t *pi_runs,
1836 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1837 ft_style_t *p_style )
1839 uint32_t i_string_length = 0;
1841 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1842 *psz_text_out += i_string_length;
1844 if( ppp_styles && ppi_run_lengths )
1850 *ppp_styles = (ft_style_t **)
1851 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1853 else if( *pi_runs == 1 )
1855 *ppp_styles = (ft_style_t **)
1856 malloc( *pi_runs * sizeof( ft_style_t * ) );
1859 /* We have just malloc'ed this memory successfully -
1860 * *pi_runs HAS to be within the memory area of *ppp_styles */
1863 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1867 if( *ppi_run_lengths )
1869 *ppi_run_lengths = (uint32_t *)
1870 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1872 else if( *pi_runs == 1 )
1874 *ppi_run_lengths = (uint32_t *)
1875 malloc( *pi_runs * sizeof( uint32_t ) );
1878 /* same remarks here */
1879 if( *ppi_run_lengths )
1881 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1884 /* If we couldn't use the p_style argument due to memory allocation
1885 * problems above, release it here.
1887 if( p_style ) DeleteStyle( p_style );
1890 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1891 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1893 /* Karaoke tags _PRECEDE_ the text they specify a duration
1894 * for, therefore we are working out the length for the
1895 * previous tag, and first time through we have nothing
1897 if( pi_k_run_lengths )
1902 /* Work out how many characters are presently in the string
1904 for( i = 0; i < i_runs; i++ )
1905 i_chars += pi_run_lengths[ i ];
1907 /* Subtract away those we've already allocated to other
1910 for( i = 0; i < i_k_runs; i++ )
1911 i_chars -= pi_k_run_lengths[ i ];
1913 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1917 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1918 uint32_t **ppi_k_run_lengths,
1919 uint32_t **ppi_k_durations )
1921 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1923 char *psz_name = xml_ReaderName( p_xml_reader );
1924 char *psz_value = xml_ReaderValue( p_xml_reader );
1926 if( psz_name && psz_value &&
1927 !strcasecmp( "t", psz_name ) )
1929 if( ppi_k_durations && ppi_k_run_lengths )
1933 if( *ppi_k_durations )
1935 *ppi_k_durations = (uint32_t *)
1936 realloc( *ppi_k_durations,
1937 *pi_k_runs * sizeof( uint32_t ) );
1939 else if( *pi_k_runs == 1 )
1941 *ppi_k_durations = (uint32_t *)
1942 malloc( *pi_k_runs * sizeof( uint32_t ) );
1945 if( *ppi_k_run_lengths )
1947 *ppi_k_run_lengths = (uint32_t *)
1948 realloc( *ppi_k_run_lengths,
1949 *pi_k_runs * sizeof( uint32_t ) );
1951 else if( *pi_k_runs == 1 )
1953 *ppi_k_run_lengths = (uint32_t *)
1954 malloc( *pi_k_runs * sizeof( uint32_t ) );
1956 if( *ppi_k_durations )
1957 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1959 if( *ppi_k_run_lengths )
1960 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1963 if( psz_name ) free( psz_name );
1964 if( psz_value ) free( psz_value );
1968 static int ProcessNodes( filter_t *p_filter,
1969 xml_reader_t *p_xml_reader,
1970 text_style_t *p_font_style,
1975 uint32_t **ppi_run_lengths,
1976 ft_style_t ***ppp_styles,
1978 vlc_bool_t b_karaoke,
1979 uint32_t *pi_k_runs,
1980 uint32_t **ppi_k_run_lengths,
1981 uint32_t **ppi_k_durations )
1983 int rv = VLC_SUCCESS;
1984 filter_sys_t *p_sys = p_filter->p_sys;
1985 uint32_t *psz_text_orig = psz_text;
1986 font_stack_t *p_fonts = NULL;
1990 char *psz_node = NULL;
1992 vlc_bool_t b_italic = VLC_FALSE;
1993 vlc_bool_t b_bold = VLC_FALSE;
1994 vlc_bool_t b_uline = VLC_FALSE;
1996 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1997 i_scale = val.i_int;
2001 rv = PushFont( &p_fonts,
2002 p_font_style->psz_fontname,
2003 p_font_style->i_font_size * i_scale / 1000,
2004 (p_font_style->i_font_color & 0xffffff) |
2005 ((p_font_style->i_font_alpha & 0xff) << 24),
2006 (p_font_style->i_karaoke_background_color & 0xffffff) |
2007 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2009 if( p_font_style->i_style_flags & STYLE_BOLD )
2011 if( p_font_style->i_style_flags & STYLE_ITALIC )
2012 b_italic = VLC_TRUE;
2013 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2018 rv = PushFont( &p_fonts,
2024 if( rv != VLC_SUCCESS )
2027 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2029 switch ( xml_ReaderNodeType( p_xml_reader ) )
2031 case XML_READER_NONE:
2033 case XML_READER_ENDELEM:
2034 psz_node = xml_ReaderName( p_xml_reader );
2038 if( !strcasecmp( "font", psz_node ) )
2039 PopFont( &p_fonts );
2040 else if( !strcasecmp( "b", psz_node ) )
2042 else if( !strcasecmp( "i", psz_node ) )
2043 b_italic = VLC_FALSE;
2044 else if( !strcasecmp( "u", psz_node ) )
2045 b_uline = VLC_FALSE;
2050 case XML_READER_STARTELEM:
2051 psz_node = xml_ReaderName( p_xml_reader );
2054 if( !strcasecmp( "font", psz_node ) )
2055 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2056 else if( !strcasecmp( "b", psz_node ) )
2058 else if( !strcasecmp( "i", psz_node ) )
2059 b_italic = VLC_TRUE;
2060 else if( !strcasecmp( "u", psz_node ) )
2062 else if( !strcasecmp( "br", psz_node ) )
2064 SetupLine( p_filter, "\n", &psz_text,
2065 pi_runs, ppi_run_lengths, ppp_styles,
2066 GetStyleFromFontStack( p_sys,
2072 else if( !strcasecmp( "k", psz_node ) )
2074 /* Only valid in karaoke */
2077 if( *pi_k_runs > 0 )
2079 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2080 *pi_k_runs, *ppi_k_run_lengths );
2082 SetupKaraoke( p_xml_reader, pi_k_runs,
2083 ppi_k_run_lengths, ppi_k_durations );
2090 case XML_READER_TEXT:
2091 psz_node = xml_ReaderValue( p_xml_reader );
2094 /* Turn any multiple-whitespaces into single spaces */
2095 char *s = strpbrk( psz_node, "\t\r\n " );
2098 int i_whitespace = strspn( s, "\t\r\n " );
2100 if( i_whitespace > 1 )
2103 strlen( s ) - i_whitespace + 1 );
2106 s = strpbrk( s, "\t\r\n " );
2108 SetupLine( p_filter, psz_node, &psz_text,
2109 pi_runs, ppi_run_lengths, ppp_styles,
2110 GetStyleFromFontStack( p_sys,
2119 if( rv != VLC_SUCCESS )
2121 psz_text = psz_text_orig;
2127 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2128 *pi_k_runs, *ppi_k_run_lengths );
2131 *pi_len = psz_text - psz_text_orig;
2133 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2138 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2142 for( k=0; k < p_sys->i_font_attachments; k++ )
2144 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2146 FT_Face p_face = NULL;
2148 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2156 vlc_bool_t match = !strcasecmp( p_face->family_name,
2157 p_style->psz_fontname );
2159 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2160 match = match && p_style->b_bold;
2162 match = match && !p_style->b_bold;
2164 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2165 match = match && p_style->b_italic;
2167 match = match && !p_style->b_italic;
2175 FT_Done_Face( p_face );
2180 return VLC_EGENERIC;
2183 static int CheckIfFontBuildComplete( filter_t *p_filter )
2185 filter_sys_t *p_sys = p_filter->p_sys;
2186 vlc_object_t *p_fb = vlc_object_find_name( p_filter->p_libvlc,
2191 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
2194 if( VLC_SUCCESS == var_Get( p_fb, "build-done", &val ))
2196 p_sys->b_fontconfig_ok = val.b_bool;
2198 if( p_sys->b_fontconfig_ok )
2201 p_sys->p_fontconfig = FcConfigGetCurrent();
2204 msg_Dbg( p_filter, "Font Build still not complete" );
2206 vlc_mutex_unlock( lock );
2207 vlc_object_release( p_fb );
2211 return VLC_EGENERIC;
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,
2225 vlc_bool_t b_karaoke,
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" );
2286 if( p_levels ) free( p_levels );
2287 if( p_old_positions ) free( p_old_positions );
2288 if( p_new_positions ) free( p_new_positions );
2289 if( p_fribidi_string ) free( p_fribidi_string );
2290 if( pp_char_styles_new ) free( pp_char_styles_new );
2291 if( pi_karaoke_bar ) 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 ];
2399 if( p_levels ) free( p_levels );
2400 if( p_new_positions ) 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_sys->b_fontconfig_ok )
2432 if( VLC_EGENERIC == CheckIfFontBuildComplete( p_filter ))
2433 msg_Err( p_filter, "Can't find FontBuilder thread!" );
2436 if( ! p_face && p_sys->b_fontconfig_ok )
2439 vlc_mutex_lock( &p_sys->fontconfig_lock );
2441 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2442 p_style->psz_fontname,
2446 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2448 if( psz_fontfile && ! *psz_fontfile )
2450 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2451 " so using default font", p_style->psz_fontname,
2452 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2453 (p_style->b_bold ? "(Bold)" :
2454 (p_style->b_italic ? "(Italic)" : ""))) );
2455 free( psz_fontfile );
2456 psz_fontfile = NULL;
2461 if( FT_New_Face( p_sys->p_library,
2462 psz_fontfile, i_idx, &p_face ) )
2464 free( psz_fontfile );
2465 free( pp_char_styles );
2466 #if defined(HAVE_FRIBIDI)
2469 if( pi_karaoke_bar )
2470 free( pi_karaoke_bar );
2471 return VLC_EGENERIC;
2473 free( psz_fontfile );
2477 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2479 /* We've loaded a font face which is unhelpful for actually
2480 * rendering text - fallback to the default one.
2482 FT_Done_Face( p_face );
2486 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2487 ft_encoding_unicode ) ||
2488 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2489 p_style->i_font_size ) )
2491 if( p_face ) FT_Done_Face( p_face );
2492 free( pp_char_styles );
2493 #if defined(HAVE_FRIBIDI)
2496 if( pi_karaoke_bar )
2497 free( pi_karaoke_bar );
2498 return VLC_EGENERIC;
2500 p_sys->i_use_kerning =
2501 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2504 uint32_t *psz_unicode = (uint32_t *)
2505 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2508 msg_Err( p_filter, "out of memory" );
2509 if( p_face ) FT_Done_Face( p_face );
2510 free( pp_char_styles );
2511 free( psz_unicode );
2512 #if defined(HAVE_FRIBIDI)
2515 if( pi_karaoke_bar )
2516 free( pi_karaoke_bar );
2519 memcpy( psz_unicode, psz_text + i_prev,
2520 sizeof( uint32_t ) * ( k - i_prev ) );
2521 psz_unicode[ k - i_prev ] = 0;
2522 while( *psz_unicode )
2526 if( !(p_line = NewLine( i_len - i_prev)) )
2528 msg_Err( p_filter, "out of memory" );
2529 if( p_face ) FT_Done_Face( p_face );
2530 free( pp_char_styles );
2531 free( psz_unicode );
2532 #if defined(HAVE_FRIBIDI)
2535 if( pi_karaoke_bar )
2536 free( pi_karaoke_bar );
2539 /* New Color mode only works in YUVA rendering mode --
2540 * (RGB mode has palette constraints on it). We therefore
2541 * need to populate the legacy colour fields also.
2543 p_line->b_new_color_mode = VLC_TRUE;
2544 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2545 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2546 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2547 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2548 p_line->p_next = NULL;
2550 i_pen_y += tmp_result.y;
2554 if( p_prev ) p_prev->p_next = p_line;
2555 else *pp_lines = p_line;
2558 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2559 p_style->i_font_color, p_style->b_underline,
2560 p_style->i_karaoke_bg_color,
2561 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2562 &tmp_result ) != VLC_SUCCESS )
2564 if( p_face ) FT_Done_Face( p_face );
2565 free( pp_char_styles );
2566 free( psz_unicode );
2567 #if defined(HAVE_FRIBIDI)
2570 if( pi_karaoke_bar )
2571 free( pi_karaoke_bar );
2572 return VLC_EGENERIC;
2577 p_result->x = __MAX( p_result->x, tmp_result.x );
2578 p_result->y += tmp_result.y;
2583 if( *psz_unicode == '\n')
2587 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2589 *c_ptr = *(c_ptr+1);
2594 free( psz_unicode );
2595 if( p_face ) FT_Done_Face( p_face );
2599 free( pp_char_styles );
2600 #if defined(HAVE_FRIBIDI)
2605 p_result->x = __MAX( p_result->x, tmp_result.x );
2606 p_result->y += tmp_result.y;
2609 if( pi_karaoke_bar )
2612 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2614 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2616 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2620 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2622 /* 100% BG colour will render faster if we
2623 * instead make it 100% FG colour, so leave
2624 * the ratio alone and copy the value across
2626 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2630 if( pi_karaoke_bar[ i ] & 0x80 )
2632 /* Swap Left and Right sides over for Right aligned
2633 * language text (eg. Arabic, Hebrew)
2635 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2637 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2638 p_line->p_bg_rgb[ k ] = i_tmp;
2640 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2643 /* Jump over the '\n' at the line-end */
2646 free( pi_karaoke_bar );
2652 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2653 subpicture_region_t *p_region_in )
2655 int rv = VLC_SUCCESS;
2656 stream_t *p_sub = NULL;
2657 xml_t *p_xml = NULL;
2658 xml_reader_t *p_xml_reader = NULL;
2660 if( !p_region_in || !p_region_in->psz_html )
2661 return VLC_EGENERIC;
2663 /* Reset the default fontsize in case screen metrics have changed */
2664 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2666 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2667 (uint8_t *) p_region_in->psz_html,
2668 strlen( p_region_in->psz_html ),
2672 p_xml = xml_Create( p_filter );
2675 vlc_bool_t b_karaoke = VLC_FALSE;
2677 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2680 /* Look for Root Node */
2681 if( xml_ReaderRead( p_xml_reader ) == 1 )
2683 char *psz_node = xml_ReaderName( p_xml_reader );
2685 if( !strcasecmp( "karaoke", psz_node ) )
2687 /* We're going to have to render the text a number
2688 * of times to show the progress marker on the text.
2690 var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2691 b_karaoke = VLC_TRUE;
2693 else if( !strcasecmp( "text", psz_node ) )
2695 b_karaoke = VLC_FALSE;
2699 /* Only text and karaoke tags are supported */
2700 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2701 xml_ReaderDelete( p_xml, p_xml_reader );
2702 p_xml_reader = NULL;
2714 uint32_t i_runs = 0;
2715 uint32_t i_k_runs = 0;
2716 uint32_t *pi_run_lengths = NULL;
2717 uint32_t *pi_k_run_lengths = NULL;
2718 uint32_t *pi_k_durations = NULL;
2719 ft_style_t **pp_styles = NULL;
2721 line_desc_t *p_lines = NULL;
2723 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2724 sizeof( uint32_t ) );
2729 rv = ProcessNodes( p_filter, p_xml_reader,
2730 p_region_in->p_style, psz_text, &i_len,
2731 &i_runs, &pi_run_lengths, &pp_styles,
2732 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2735 p_region_out->i_x = p_region_in->i_x;
2736 p_region_out->i_y = p_region_in->i_y;
2738 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2740 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2741 pi_run_lengths, pp_styles, &p_lines, &result,
2742 b_karaoke, i_k_runs, pi_k_run_lengths,
2746 for( k=0; k<i_runs; k++)
2747 DeleteStyle( pp_styles[k] );
2749 free( pi_run_lengths );
2752 /* Don't attempt to render text that couldn't be layed out
2755 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2757 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2759 Render( p_filter, p_region_out, p_lines,
2760 result.x, result.y );
2764 RenderYUVA( p_filter, p_region_out, p_lines,
2765 result.x, result.y );
2769 FreeLines( p_lines );
2771 xml_ReaderDelete( p_xml, p_xml_reader );
2773 xml_Delete( p_xml );
2775 stream_Delete( p_sub );
2781 static char* FontConfig_Select( FcConfig* priv, const char* family,
2782 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2785 FcPattern *pat, *p_pat;
2789 pat = FcPatternCreate();
2790 if (!pat) return NULL;
2792 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2793 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2794 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2795 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2797 FcDefaultSubstitute( pat );
2799 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2801 FcPatternDestroy( pat );
2805 p_pat = FcFontMatch( priv, pat, &result );
2806 FcPatternDestroy( pat );
2807 if( !p_pat ) return NULL;
2809 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2810 || ( val_b != FcTrue ) )
2812 FcPatternDestroy( p_pat );
2815 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2820 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2822 FcPatternDestroy( p_pat );
2827 if( strcasecmp((const char*)val_s, family ) != 0 )
2828 msg_Warn( p_filter, "fontconfig: selected font family is not"
2829 "the requested one: '%s' != '%s'\n",
2830 (const char*)val_s, family );
2833 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2835 FcPatternDestroy( p_pat );
2839 FcPatternDestroy( p_pat );
2840 return strdup( (const char*)val_s );
2844 static void FreeLine( line_desc_t *p_line )
2847 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2849 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2851 free( p_line->pp_glyphs );
2852 free( p_line->p_glyph_pos );
2853 free( p_line->p_fg_rgb );
2854 free( p_line->p_bg_rgb );
2855 free( p_line->p_fg_bg_ratio );
2856 free( p_line->pi_underline_offset );
2857 free( p_line->pi_underline_thickness );
2861 static void FreeLines( line_desc_t *p_lines )
2863 line_desc_t *p_line, *p_next;
2865 if( !p_lines ) return;
2867 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2869 p_next = p_line->p_next;
2874 static line_desc_t *NewLine( int i_count )
2876 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2878 if( !p_line ) return NULL;
2879 p_line->i_height = 0;
2880 p_line->i_width = 0;
2881 p_line->p_next = NULL;
2883 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2884 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2885 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2886 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2887 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2888 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2889 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2890 if( ( p_line->pp_glyphs == NULL ) ||
2891 ( p_line->p_glyph_pos == NULL ) ||
2892 ( p_line->p_fg_rgb == NULL ) ||
2893 ( p_line->p_bg_rgb == NULL ) ||
2894 ( p_line->p_fg_bg_ratio == NULL ) ||
2895 ( p_line->pi_underline_offset == NULL ) ||
2896 ( p_line->pi_underline_thickness == NULL ) )
2898 if( p_line->pi_underline_thickness )
2899 free( p_line->pi_underline_thickness );
2900 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2901 if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2902 if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2903 if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2904 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2905 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2909 p_line->pp_glyphs[0] = NULL;
2910 p_line->b_new_color_mode = VLC_FALSE;
2915 static int GetFontSize( filter_t *p_filter )
2917 filter_sys_t *p_sys = p_filter->p_sys;
2921 if( p_sys->i_default_font_size )
2923 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2924 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2926 i_size = p_sys->i_default_font_size;
2930 var_Get( p_filter, "freetype-rel-fontsize", &val );
2931 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2932 p_filter->p_sys->i_display_height =
2933 p_filter->fmt_out.video.i_height;
2937 msg_Warn( p_filter, "invalid fontsize, using 12" );
2938 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2939 i_size = 12 * val.i_int / 1000;
2946 static int SetFontSize( filter_t *p_filter, int i_size )
2948 filter_sys_t *p_sys = p_filter->p_sys;
2952 i_size = GetFontSize( p_filter );
2954 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2957 p_sys->i_font_size = i_size;
2959 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2961 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2962 return VLC_EGENERIC;
2968 static void YUVFromRGB( uint32_t i_argb,
2969 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2971 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2972 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2973 int i_blue = ( i_argb & 0x000000ff );
2975 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2976 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2977 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2978 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2979 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2980 -585 * i_blue + 4096 + 1048576) >> 13, 240);