1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
37 #include <vlc_block.h>
38 #include <vlc_filter.h>
39 #include <vlc_stream.h>
41 #include <vlc_input.h>
47 #include FT_FREETYPE_H
49 #define FT_FLOOR(X) ((X & -64) >> 6)
50 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
51 #define FT_MulFix(v, s) (((v)*(s))>>16)
54 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
55 #define FC_DEFAULT_FONT "Lucida Grande"
56 #elif defined( SYS_BEOS )
57 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
58 #define FC_DEFAULT_FONT "Swiss"
59 #elif defined( WIN32 )
60 #define DEFAULT_FONT "" /* Default font found at run-time */
61 #define FC_DEFAULT_FONT "Arial"
63 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
64 #define FC_DEFAULT_FONT "Serif Bold"
67 #if defined(HAVE_FRIBIDI)
68 #include <fribidi/fribidi.h>
71 #ifdef HAVE_FONTCONFIG
72 #include <fontconfig/fontconfig.h>
75 typedef struct line_desc_t line_desc_t;
77 /*****************************************************************************
79 *****************************************************************************/
80 static int Create ( vlc_object_t * );
81 static void Destroy( vlc_object_t * );
83 static int LoadFontsFromAttachments( filter_t *p_filter );
85 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
86 static int RenderText( filter_t *, subpicture_region_t *,
87 subpicture_region_t * );
88 #ifdef HAVE_FONTCONFIG
89 static int RenderHtml( filter_t *, subpicture_region_t *,
90 subpicture_region_t * );
91 static char *FontConfig_Select( FcConfig *, const char *,
93 static int 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, 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, true );
169 /* hook to the color values list, with default 0x00ffffff = white */
170 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
171 COLOR_LONGTEXT, 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, false );
176 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
177 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
178 EFFECT_LONGTEXT, false );
179 change_integer_list( pi_effects, ppsz_effects_text, 0 );
181 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
182 YUVP_LONGTEXT, 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 bool 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 */
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 */
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 bool 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 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 = 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", 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 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 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", true );
523 FcConfigDestroy( p_fontconfig );
524 vlc_mutex_unlock( lock );
526 vlc_object_detach( p_this );
527 vlc_object_release( p_this );
532 /*****************************************************************************
533 * Make any TTF/OTF fonts present in the attachments of the media file
534 * and store them for later use by the FreeType Engine
535 *****************************************************************************/
536 static int LoadFontsFromAttachments( filter_t *p_filter )
538 filter_sys_t *p_sys = p_filter->p_sys;
539 input_thread_t *p_input;
540 input_attachment_t **pp_attachments;
541 int i_attachments_cnt;
543 int rv = VLC_SUCCESS;
545 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
549 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
551 vlc_object_release(p_input);
555 p_sys->i_font_attachments = 0;
556 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
557 if(! p_sys->pp_font_attachments )
560 for( k = 0; k < i_attachments_cnt; k++ )
562 input_attachment_t *p_attach = pp_attachments[k];
564 if( p_sys->pp_font_attachments )
566 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
567 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
568 ( p_attach->i_data > 0 ) &&
569 ( p_attach->p_data != NULL ) )
571 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
575 vlc_input_attachment_Delete( p_attach );
580 vlc_input_attachment_Delete( p_attach );
583 free( pp_attachments );
585 vlc_object_release(p_input);
590 /*****************************************************************************
591 * Render: place string in picture
592 *****************************************************************************
593 * This function merges the previously rendered freetype glyphs into a picture
594 *****************************************************************************/
595 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
596 line_desc_t *p_line, int i_width, int i_height )
598 static uint8_t pi_gamma[16] =
599 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
600 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
604 int i, x, y, i_pitch;
605 uint8_t i_y; /* YUV values, derived from incoming RGB */
607 subpicture_region_t *p_region_tmp;
609 /* Create a new subpicture region */
610 memset( &fmt, 0, sizeof(video_format_t) );
611 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
613 fmt.i_width = fmt.i_visible_width = i_width + 4;
614 fmt.i_height = fmt.i_visible_height = i_height + 4;
615 if( p_region->fmt.i_visible_width > 0 )
616 fmt.i_visible_width = p_region->fmt.i_visible_width;
617 if( p_region->fmt.i_visible_height > 0 )
618 fmt.i_visible_height = p_region->fmt.i_visible_height;
619 fmt.i_x_offset = fmt.i_y_offset = 0;
620 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
623 msg_Err( p_filter, "cannot allocate SPU region" );
627 p_region->fmt = p_region_tmp->fmt;
628 p_region->picture = p_region_tmp->picture;
629 free( p_region_tmp );
631 /* Calculate text color components */
632 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
633 25 * p_line->i_blue + 128) >> 8) + 16;
634 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
635 112 * p_line->i_blue + 128) >> 8) + 128;
636 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
637 18 * p_line->i_blue + 128) >> 8) + 128;
640 fmt.p_palette->i_entries = 16;
641 for( i = 0; i < 8; i++ )
643 fmt.p_palette->palette[i][0] = 0;
644 fmt.p_palette->palette[i][1] = 0x80;
645 fmt.p_palette->palette[i][2] = 0x80;
646 fmt.p_palette->palette[i][3] = pi_gamma[i];
647 fmt.p_palette->palette[i][3] =
648 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
650 for( i = 8; i < fmt.p_palette->i_entries; i++ )
652 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
653 fmt.p_palette->palette[i][1] = i_u;
654 fmt.p_palette->palette[i][2] = i_v;
655 fmt.p_palette->palette[i][3] = pi_gamma[i];
656 fmt.p_palette->palette[i][3] =
657 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
660 p_dst = p_region->picture.Y_PIXELS;
661 i_pitch = p_region->picture.Y_PITCH;
663 /* Initialize the region pixels */
664 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
666 for( ; p_line != NULL; p_line = p_line->p_next )
668 int i_glyph_tmax = 0;
669 int i_bitmap_offset, i_offset, i_align_offset = 0;
670 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
672 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
673 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
676 if( p_line->i_width < i_width )
678 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
680 i_align_offset = i_width - p_line->i_width;
682 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
684 i_align_offset = ( i_width - p_line->i_width ) / 2;
688 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
690 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
692 i_offset = ( p_line->p_glyph_pos[ i ].y +
693 i_glyph_tmax - p_glyph->top + 2 ) *
694 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
697 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
699 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
701 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
703 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
710 /* Outlining (find something better than nearest neighbour filtering ?) */
713 uint8_t *p_dst = p_region->picture.Y_PIXELS;
714 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
715 uint8_t left, current;
717 for( y = 1; y < (int)fmt.i_height - 1; y++ )
719 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
720 p_dst += p_region->picture.Y_PITCH;
723 for( x = 1; x < (int)fmt.i_width - 1; x++ )
726 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
727 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;
731 memset( p_top, 0, fmt.i_width );
737 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
738 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
739 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
740 int i_glyph_tmax, int i_align_offset,
741 uint8_t i_y, uint8_t i_u, uint8_t i_v,
742 subpicture_region_t *p_region)
746 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
748 p_dst_y = p_region->picture.Y_PIXELS;
749 p_dst_u = p_region->picture.U_PIXELS;
750 p_dst_v = p_region->picture.V_PIXELS;
751 p_dst_a = p_region->picture.A_PIXELS;
752 i_pitch = p_region->picture.A_PITCH;
754 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
755 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
757 for( y = 0; y < i_line_thickness; y++ )
759 int i_extra = p_this_glyph->bitmap.width;
763 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
764 (p_this_glyph_pos->x + p_this_glyph->left);
766 for( x = 0; x < i_extra; x++ )
770 /* break the underline around the tails of any glyphs which cross it */
771 for( z = x - i_line_thickness;
772 z < x + i_line_thickness && b_ok;
775 if( p_next_glyph && ( z >= i_extra ) )
777 int i_row = i_line_offset + p_next_glyph->top + y;
779 if( ( p_next_glyph->bitmap.rows > i_row ) &&
780 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
785 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
787 int i_row = i_line_offset + p_this_glyph->top + y;
789 if( ( p_this_glyph->bitmap.rows > i_row ) &&
790 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
799 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
800 p_dst_u[i_offset+x] = i_u;
801 p_dst_v[i_offset+x] = i_v;
802 p_dst_a[i_offset+x] = 255;
809 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
811 uint8_t *p_dst = p_region->picture.A_PIXELS;
812 int i_pitch = p_region->picture.A_PITCH;
815 for( ; p_line != NULL; p_line = p_line->p_next )
817 int i_glyph_tmax=0, i = 0;
818 int i_bitmap_offset, i_offset, i_align_offset = 0;
819 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
821 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
822 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
825 if( p_line->i_width < i_width )
827 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
829 i_align_offset = i_width - p_line->i_width;
831 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
833 i_align_offset = ( i_width - p_line->i_width ) / 2;
837 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
839 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
841 i_offset = ( p_line->p_glyph_pos[ i ].y +
842 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
843 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
844 i_align_offset +xoffset;
846 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
848 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
850 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
851 if( p_dst[i_offset+x] <
852 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
854 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
863 /*****************************************************************************
864 * Render: place string in picture
865 *****************************************************************************
866 * This function merges the previously rendered freetype glyphs into a picture
867 *****************************************************************************/
868 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
869 line_desc_t *p_line, int i_width, int i_height )
871 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
873 int i, x, y, i_pitch, i_alpha;
874 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
875 subpicture_region_t *p_region_tmp;
877 if( i_width == 0 || i_height == 0 )
880 /* Create a new subpicture region */
881 memset( &fmt, 0, sizeof(video_format_t) );
882 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
884 fmt.i_width = fmt.i_visible_width = i_width + 6;
885 fmt.i_height = fmt.i_visible_height = i_height + 6;
886 if( p_region->fmt.i_visible_width > 0 )
887 fmt.i_visible_width = p_region->fmt.i_visible_width;
888 if( p_region->fmt.i_visible_height > 0 )
889 fmt.i_visible_height = p_region->fmt.i_visible_height;
890 fmt.i_x_offset = fmt.i_y_offset = 0;
891 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
894 msg_Err( p_filter, "cannot allocate SPU region" );
898 p_region->fmt = p_region_tmp->fmt;
899 p_region->picture = p_region_tmp->picture;
900 free( p_region_tmp );
902 /* Calculate text color components */
903 YUVFromRGB( (p_line->i_red << 16) |
904 (p_line->i_green << 8) |
907 i_alpha = p_line->i_alpha;
909 p_dst_y = p_region->picture.Y_PIXELS;
910 p_dst_u = p_region->picture.U_PIXELS;
911 p_dst_v = p_region->picture.V_PIXELS;
912 p_dst_a = p_region->picture.A_PIXELS;
913 i_pitch = p_region->picture.A_PITCH;
915 /* Initialize the region pixels */
916 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
918 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
919 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
920 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
921 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
925 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
926 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
927 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
928 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
930 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
931 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
933 DrawBlack( p_line, i_width, p_region, 0, 0);
934 DrawBlack( p_line, i_width, p_region, -1, 0);
935 DrawBlack( p_line, i_width, p_region, 0, -1);
936 DrawBlack( p_line, i_width, p_region, 1, 0);
937 DrawBlack( p_line, i_width, p_region, 0, 1);
940 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
942 DrawBlack( p_line, i_width, p_region, -1, -1);
943 DrawBlack( p_line, i_width, p_region, -1, 1);
944 DrawBlack( p_line, i_width, p_region, 1, -1);
945 DrawBlack( p_line, i_width, p_region, 1, 1);
947 DrawBlack( p_line, i_width, p_region, -2, 0);
948 DrawBlack( p_line, i_width, p_region, 0, -2);
949 DrawBlack( p_line, i_width, p_region, 2, 0);
950 DrawBlack( p_line, i_width, p_region, 0, 2);
952 DrawBlack( p_line, i_width, p_region, -2, -2);
953 DrawBlack( p_line, i_width, p_region, -2, 2);
954 DrawBlack( p_line, i_width, p_region, 2, -2);
955 DrawBlack( p_line, i_width, p_region, 2, 2);
957 DrawBlack( p_line, i_width, p_region, -3, 0);
958 DrawBlack( p_line, i_width, p_region, 0, -3);
959 DrawBlack( p_line, i_width, p_region, 3, 0);
960 DrawBlack( p_line, i_width, p_region, 0, 3);
963 for( ; p_line != NULL; p_line = p_line->p_next )
965 int i_glyph_tmax = 0;
966 int i_bitmap_offset, i_offset, i_align_offset = 0;
967 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
969 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
970 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
973 if( p_line->i_width < i_width )
975 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
977 i_align_offset = i_width - p_line->i_width;
979 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
981 i_align_offset = ( i_width - p_line->i_width ) / 2;
985 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
987 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
989 i_offset = ( p_line->p_glyph_pos[ i ].y +
990 i_glyph_tmax - p_glyph->top + 3 ) *
991 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
994 if( p_line->b_new_color_mode )
996 /* Every glyph can (and in fact must) have its own color */
997 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1000 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1002 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1004 uint8_t i_y_local = i_y;
1005 uint8_t i_u_local = i_u;
1006 uint8_t i_v_local = i_v;
1008 if( p_line->p_fg_bg_ratio != 0x00 )
1010 int i_split = p_glyph->bitmap.width *
1011 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1015 YUVFromRGB( p_line->p_bg_rgb[ i ],
1016 &i_y_local, &i_u_local, &i_v_local );
1020 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1022 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1023 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1025 p_dst_u[i_offset+x] = i_u;
1026 p_dst_v[i_offset+x] = i_v;
1028 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1029 p_dst_a[i_offset+x] = 0xff;
1032 i_offset += i_pitch;
1035 if( p_line->pi_underline_thickness[ i ] )
1037 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1038 p_line->pi_underline_offset[ i ],
1039 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1040 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1041 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1042 i_glyph_tmax, i_align_offset,
1049 /* Apply the alpha setting */
1050 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1051 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1057 * This function renders a text subpicture region into another one.
1058 * It also calculates the size needed for this string, and renders the
1059 * needed glyphs into memory. It is used as pf_add_string callback in
1060 * the vout method by this module
1062 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1063 subpicture_region_t *p_region_in )
1065 filter_sys_t *p_sys = p_filter->p_sys;
1066 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1067 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1068 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1069 int i_string_length;
1071 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1072 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1082 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1083 psz_string = p_region_in->psz_text;
1084 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1086 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1087 i_scale = val.i_int;
1089 if( p_region_in->p_style )
1091 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1092 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1093 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1097 i_font_color = p_sys->i_font_color;
1098 i_font_alpha = 255 - p_sys->i_font_opacity;
1099 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1102 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1103 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1104 SetFontSize( p_filter, i_font_size );
1106 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1107 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1108 i_blue = i_font_color & 0x000000FF;
1110 result.x = result.y = 0;
1111 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1113 psz_unicode = psz_unicode_orig =
1114 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1115 if( psz_unicode == NULL )
1117 msg_Err( p_filter, "out of memory" );
1120 #if defined(WORDS_BIGENDIAN)
1121 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1123 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1125 if( iconv_handle == (vlc_iconv_t)-1 )
1127 msg_Warn( p_filter, "unable to do conversion" );
1133 const char *p_in_buffer = psz_string;
1134 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1135 i_in_bytes = strlen( psz_string );
1136 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1137 i_out_bytes_left = i_out_bytes;
1138 p_out_buffer = (char *)psz_unicode;
1139 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1141 &p_out_buffer, &i_out_bytes_left );
1143 vlc_iconv_close( iconv_handle );
1147 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1148 "bytes left %u", (unsigned)i_in_bytes );
1151 *(uint32_t*)p_out_buffer = 0;
1152 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1155 #if defined(HAVE_FRIBIDI)
1157 uint32_t *p_fribidi_string;
1158 int32_t start_pos, pos = 0;
1160 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1161 if( !p_fribidi_string )
1163 msg_Err( p_filter, "out of memory" );
1167 /* Do bidi conversion line-by-line */
1168 while( pos < i_string_length )
1170 while( pos < i_string_length )
1172 i_char = psz_unicode[pos];
1173 if (i_char != '\r' && i_char != '\n')
1175 p_fribidi_string[pos] = i_char;
1179 while( pos < i_string_length )
1181 i_char = psz_unicode[pos];
1182 if (i_char == '\r' || i_char == '\n')
1186 if (pos > start_pos)
1188 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1189 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1192 (FriBidiChar*)p_fribidi_string + start_pos,
1197 free( psz_unicode_orig );
1198 psz_unicode = psz_unicode_orig = p_fribidi_string;
1199 p_fribidi_string[ i_string_length ] = 0;
1203 /* Calculate relative glyph positions and a bounding box for the
1205 if( !(p_line = NewLine( strlen( psz_string ))) )
1207 msg_Err( p_filter, "out of memory" );
1211 i_pen_x = i_pen_y = 0;
1213 psz_line_start = psz_unicode;
1215 #define face p_sys->p_face
1216 #define glyph face->glyph
1218 while( *psz_unicode )
1220 i_char = *psz_unicode++;
1221 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1226 if( i_char == '\n' )
1228 psz_line_start = psz_unicode;
1229 if( !(p_next = NewLine( strlen( psz_string ))) )
1231 msg_Err( p_filter, "out of memory" );
1234 p_line->p_next = p_next;
1235 p_line->i_width = line.xMax;
1236 p_line->i_height = face->size->metrics.height >> 6;
1237 p_line->pp_glyphs[ i ] = NULL;
1238 p_line->i_alpha = i_font_alpha;
1239 p_line->i_red = i_red;
1240 p_line->i_green = i_green;
1241 p_line->i_blue = i_blue;
1244 result.x = __MAX( result.x, line.xMax );
1245 result.y += face->size->metrics.height >> 6;
1248 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1249 i_pen_y += face->size->metrics.height >> 6;
1251 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1256 i_glyph_index = FT_Get_Char_Index( face, i_char );
1257 if( p_sys->i_use_kerning && i_glyph_index
1261 FT_Get_Kerning( face, i_previous, i_glyph_index,
1262 ft_kerning_default, &delta );
1263 i_pen_x += delta.x >> 6;
1266 p_line->p_glyph_pos[ i ].x = i_pen_x;
1267 p_line->p_glyph_pos[ i ].y = i_pen_y;
1268 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1271 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1275 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1278 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1282 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1283 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1286 FT_Done_Glyph( tmp_glyph );
1289 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1292 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1293 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1294 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1296 p_line->pp_glyphs[ i ] = NULL;
1298 p_line = NewLine( strlen( psz_string ));
1299 if( p_prev ) p_prev->p_next = p_line;
1300 else p_lines = p_line;
1302 uint32_t *psz_unicode_saved = psz_unicode;
1303 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1307 if( psz_unicode == psz_line_start )
1308 { /* try harder to break that line */
1309 psz_unicode = psz_unicode_saved;
1310 while( psz_unicode > psz_line_start &&
1311 *psz_unicode != '_' && *psz_unicode != '/' &&
1312 *psz_unicode != '\\' && *psz_unicode != '.' )
1317 if( psz_unicode == psz_line_start )
1319 msg_Warn( p_filter, "unbreakable string" );
1324 *psz_unicode = '\n';
1326 psz_unicode = psz_line_start;
1329 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1332 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1333 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1335 i_previous = i_glyph_index;
1336 i_pen_x += glyph->advance.x >> 6;
1340 p_line->i_width = line.xMax;
1341 p_line->i_height = face->size->metrics.height >> 6;
1342 p_line->pp_glyphs[ i ] = NULL;
1343 p_line->i_alpha = i_font_alpha;
1344 p_line->i_red = i_red;
1345 p_line->i_green = i_green;
1346 p_line->i_blue = i_blue;
1347 result.x = __MAX( result.x, line.xMax );
1348 result.y += line.yMax - line.yMin;
1353 p_region_out->i_x = p_region_in->i_x;
1354 p_region_out->i_y = p_region_in->i_y;
1356 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1357 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1359 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1361 free( psz_unicode_orig );
1362 FreeLines( p_lines );
1366 free( psz_unicode_orig );
1367 FreeLines( p_lines );
1368 return VLC_EGENERIC;
1371 #ifdef HAVE_FONTCONFIG
1372 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1373 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1374 bool b_italic, bool b_uline )
1376 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1380 p_style->i_font_size = i_font_size;
1381 p_style->i_font_color = i_font_color;
1382 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1383 p_style->b_italic = b_italic;
1384 p_style->b_bold = b_bold;
1385 p_style->b_underline = b_uline;
1387 p_style->psz_fontname = strdup( psz_fontname );
1392 static void DeleteStyle( ft_style_t *p_style )
1396 free( p_style->psz_fontname );
1401 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1408 if(( s1->i_font_size == s2->i_font_size ) &&
1409 ( s1->i_font_color == s2->i_font_color ) &&
1410 ( s1->b_italic == s2->b_italic ) &&
1411 ( s1->b_bold == s2->b_bold ) &&
1412 ( s1->b_underline == s2->b_underline ) &&
1413 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1420 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1421 uint32_t i_color, uint32_t i_karaoke_bg_color )
1423 font_stack_t *p_new;
1426 return VLC_EGENERIC;
1428 p_new = malloc( sizeof( font_stack_t ) );
1432 p_new->p_next = NULL;
1435 p_new->psz_name = strdup( psz_name );
1437 p_new->psz_name = NULL;
1439 p_new->i_size = i_size;
1440 p_new->i_color = i_color;
1441 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1449 font_stack_t *p_last;
1451 for( p_last = *p_font;
1453 p_last = p_last->p_next )
1456 p_last->p_next = p_new;
1461 static int PopFont( font_stack_t **p_font )
1463 font_stack_t *p_last, *p_next_to_last;
1465 if( !p_font || !*p_font )
1466 return VLC_EGENERIC;
1468 p_next_to_last = NULL;
1469 for( p_last = *p_font;
1471 p_last = p_last->p_next )
1473 p_next_to_last = p_last;
1476 if( p_next_to_last )
1477 p_next_to_last->p_next = NULL;
1481 free( p_last->psz_name );
1487 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1488 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1490 font_stack_t *p_last;
1492 if( !p_font || !*p_font )
1493 return VLC_EGENERIC;
1495 for( p_last=*p_font;
1497 p_last=p_last->p_next )
1500 *psz_name = p_last->psz_name;
1501 *i_size = p_last->i_size;
1502 *i_color = p_last->i_color;
1503 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1508 static void IconvText( filter_t *p_filter, const char *psz_string,
1509 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1511 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1513 /* If memory hasn't been allocated for our output string, allocate it here
1514 * - the calling function must now be responsible for freeing it.
1516 if( !*ppsz_unicode )
1517 *ppsz_unicode = (uint32_t *)
1518 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1520 /* We don't need to handle a NULL pointer in *ppsz_unicode
1521 * if we are instead testing for a non NULL value like we are here */
1525 #if defined(WORDS_BIGENDIAN)
1526 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1528 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1530 if( iconv_handle != (vlc_iconv_t)-1 )
1532 char *p_in_buffer, *p_out_buffer;
1533 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1534 i_in_bytes = strlen( psz_string );
1535 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1536 i_out_bytes_left = i_out_bytes;
1537 p_in_buffer = (char *) psz_string;
1538 p_out_buffer = (char *) *ppsz_unicode;
1539 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1540 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1542 vlc_iconv_close( iconv_handle );
1546 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1547 "bytes left %u", (unsigned)i_in_bytes );
1551 *(uint32_t*)p_out_buffer = 0;
1553 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1558 msg_Warn( p_filter, "unable to do conversion" );
1563 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1564 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1567 ft_style_t *p_style = NULL;
1569 char *psz_fontname = NULL;
1570 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1571 uint32_t i_karaoke_bg_color = i_font_color;
1572 int i_font_size = p_sys->i_font_size;
1574 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1575 &i_font_color, &i_karaoke_bg_color ))
1577 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1578 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1583 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1584 bool b_uline, int i_karaoke_bgcolor,
1585 line_desc_t *p_line, uint32_t *psz_unicode,
1586 int *pi_pen_x, int i_pen_y, int *pi_start,
1587 FT_Vector *p_result )
1592 bool b_first_on_line = true;
1595 int i_pen_x_start = *pi_pen_x;
1597 uint32_t *psz_unicode_start = psz_unicode;
1599 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1601 /* Account for part of line already in position */
1602 for( i=0; i<*pi_start; i++ )
1606 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1607 ft_glyph_bbox_pixels, &glyph_size );
1609 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1610 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1611 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1612 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1618 b_first_on_line = false;
1620 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1626 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1627 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1631 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1632 ft_kerning_default, &delta );
1633 *pi_pen_x += delta.x >> 6;
1635 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1636 p_line->p_glyph_pos[ i ].y = i_pen_y;
1638 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1642 "unable to render text FT_Load_Glyph returned %d", i_error );
1643 p_line->pp_glyphs[ i ] = NULL;
1644 return VLC_EGENERIC;
1646 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1650 "unable to render text FT_Get_Glyph returned %d", i_error );
1651 p_line->pp_glyphs[ i ] = NULL;
1652 return VLC_EGENERIC;
1654 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1655 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1658 FT_Done_Glyph( tmp_glyph );
1663 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1664 p_face->size->metrics.y_scale));
1665 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1666 p_face->size->metrics.y_scale));
1668 p_line->pi_underline_offset[ i ] =
1669 ( aOffset < 0 ) ? -aOffset : aOffset;
1670 p_line->pi_underline_thickness[ i ] =
1671 ( aSize < 0 ) ? -aSize : aSize;
1673 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1674 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1675 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1676 p_line->p_fg_bg_ratio[ i ] = 0x00;
1678 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1679 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1680 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1682 while( --i > *pi_start )
1684 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1687 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1691 if( psz_unicode == psz_unicode_start )
1693 if( b_first_on_line )
1695 msg_Warn( p_filter, "unbreakable string" );
1696 p_line->pp_glyphs[ i ] = NULL;
1697 return VLC_EGENERIC;
1699 *pi_pen_x = i_pen_x_start;
1701 p_line->i_width = line.xMax;
1702 p_line->i_height = __MAX( p_line->i_height,
1703 p_face->size->metrics.height >> 6 );
1704 p_line->pp_glyphs[ i ] = NULL;
1706 p_result->x = __MAX( p_result->x, line.xMax );
1707 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1708 i_yMax - i_yMin ) );
1715 *psz_unicode = '\n';
1717 psz_unicode = psz_unicode_start;
1718 *pi_pen_x = i_pen_x_start;
1726 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1727 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1729 i_previous = i_glyph_index;
1730 *pi_pen_x += p_face->glyph->advance.x >> 6;
1733 p_line->i_width = line.xMax;
1734 p_line->i_height = __MAX( p_line->i_height,
1735 p_face->size->metrics.height >> 6 );
1736 p_line->pp_glyphs[ i ] = NULL;
1738 p_result->x = __MAX( p_result->x, line.xMax );
1739 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1740 line.yMax - line.yMin ) );
1744 /* Get rid of any text processed - if necessary repositioning
1745 * at the start of a new line of text
1749 *psz_unicode_start = '\0';
1751 else if( psz_unicode > psz_unicode_start )
1753 for( i=0; psz_unicode[ i ]; i++ )
1754 psz_unicode_start[ i ] = psz_unicode[ i ];
1755 psz_unicode_start[ i ] = '\0';
1761 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1762 font_stack_t **p_fonts, int i_scale )
1765 char *psz_fontname = NULL;
1766 uint32_t i_font_color = 0xffffff;
1767 int i_font_alpha = 0;
1768 uint32_t i_karaoke_bg_color = 0x00ffffff;
1769 int i_font_size = 24;
1771 /* Default all attributes to the top font in the stack -- in case not
1772 * all attributes are specified in the sub-font
1774 if( VLC_SUCCESS == PeekFont( p_fonts,
1778 &i_karaoke_bg_color ))
1780 psz_fontname = strdup( psz_fontname );
1781 i_font_size = i_font_size * 1000 / i_scale;
1783 i_font_alpha = (i_font_color >> 24) & 0xff;
1784 i_font_color &= 0x00ffffff;
1786 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1788 char *psz_name = xml_ReaderName( p_xml_reader );
1789 char *psz_value = xml_ReaderValue( p_xml_reader );
1791 if( psz_name && psz_value )
1793 if( !strcasecmp( "face", psz_name ) )
1795 free( psz_fontname );
1796 psz_fontname = strdup( psz_value );
1798 else if( !strcasecmp( "size", psz_name ) )
1800 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1802 int i_value = atoi( psz_value );
1804 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1805 i_font_size += ( i_value * i_font_size ) / 10;
1806 else if( i_value < -5 )
1807 i_font_size = - i_value;
1808 else if( i_value > 5 )
1809 i_font_size = i_value;
1812 i_font_size = atoi( psz_value );
1814 else if( !strcasecmp( "color", psz_name ) &&
1815 ( psz_value[0] == '#' ) )
1817 i_font_color = strtol( psz_value + 1, NULL, 16 );
1818 i_font_color &= 0x00ffffff;
1820 else if( !strcasecmp( "alpha", psz_name ) &&
1821 ( psz_value[0] == '#' ) )
1823 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1824 i_font_alpha &= 0xff;
1830 rv = PushFont( p_fonts,
1832 i_font_size * i_scale / 1000,
1833 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1834 i_karaoke_bg_color );
1836 free( psz_fontname );
1841 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1842 uint32_t **psz_text_out, uint32_t *pi_runs,
1843 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1844 ft_style_t *p_style )
1846 uint32_t i_string_length = 0;
1848 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1849 *psz_text_out += i_string_length;
1851 if( ppp_styles && ppi_run_lengths )
1857 *ppp_styles = (ft_style_t **)
1858 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1860 else if( *pi_runs == 1 )
1862 *ppp_styles = (ft_style_t **)
1863 malloc( *pi_runs * sizeof( ft_style_t * ) );
1866 /* We have just malloc'ed this memory successfully -
1867 * *pi_runs HAS to be within the memory area of *ppp_styles */
1870 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1874 if( *ppi_run_lengths )
1876 *ppi_run_lengths = (uint32_t *)
1877 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1879 else if( *pi_runs == 1 )
1881 *ppi_run_lengths = (uint32_t *)
1882 malloc( *pi_runs * sizeof( uint32_t ) );
1885 /* same remarks here */
1886 if( *ppi_run_lengths )
1888 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1891 /* If we couldn't use the p_style argument due to memory allocation
1892 * problems above, release it here.
1894 if( p_style ) DeleteStyle( p_style );
1897 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1898 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1900 /* Karaoke tags _PRECEDE_ the text they specify a duration
1901 * for, therefore we are working out the length for the
1902 * previous tag, and first time through we have nothing
1904 if( pi_k_run_lengths )
1909 /* Work out how many characters are presently in the string
1911 for( i = 0; i < i_runs; i++ )
1912 i_chars += pi_run_lengths[ i ];
1914 /* Subtract away those we've already allocated to other
1917 for( i = 0; i < i_k_runs; i++ )
1918 i_chars -= pi_k_run_lengths[ i ];
1920 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1924 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1925 uint32_t **ppi_k_run_lengths,
1926 uint32_t **ppi_k_durations )
1928 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1930 char *psz_name = xml_ReaderName( p_xml_reader );
1931 char *psz_value = xml_ReaderValue( p_xml_reader );
1933 if( psz_name && psz_value &&
1934 !strcasecmp( "t", psz_name ) )
1936 if( ppi_k_durations && ppi_k_run_lengths )
1940 if( *ppi_k_durations )
1942 *ppi_k_durations = (uint32_t *)
1943 realloc( *ppi_k_durations,
1944 *pi_k_runs * sizeof( uint32_t ) );
1946 else if( *pi_k_runs == 1 )
1948 *ppi_k_durations = (uint32_t *)
1949 malloc( *pi_k_runs * sizeof( uint32_t ) );
1952 if( *ppi_k_run_lengths )
1954 *ppi_k_run_lengths = (uint32_t *)
1955 realloc( *ppi_k_run_lengths,
1956 *pi_k_runs * sizeof( uint32_t ) );
1958 else if( *pi_k_runs == 1 )
1960 *ppi_k_run_lengths = (uint32_t *)
1961 malloc( *pi_k_runs * sizeof( uint32_t ) );
1963 if( *ppi_k_durations )
1964 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1966 if( *ppi_k_run_lengths )
1967 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1975 static int ProcessNodes( filter_t *p_filter,
1976 xml_reader_t *p_xml_reader,
1977 text_style_t *p_font_style,
1982 uint32_t **ppi_run_lengths,
1983 ft_style_t ***ppp_styles,
1986 uint32_t *pi_k_runs,
1987 uint32_t **ppi_k_run_lengths,
1988 uint32_t **ppi_k_durations )
1990 int rv = VLC_SUCCESS;
1991 filter_sys_t *p_sys = p_filter->p_sys;
1992 uint32_t *psz_text_orig = psz_text;
1993 font_stack_t *p_fonts = NULL;
1997 char *psz_node = NULL;
1999 bool b_italic = false;
2000 bool b_bold = false;
2001 bool b_uline = false;
2003 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2004 i_scale = val.i_int;
2008 rv = PushFont( &p_fonts,
2009 p_font_style->psz_fontname,
2010 p_font_style->i_font_size * i_scale / 1000,
2011 (p_font_style->i_font_color & 0xffffff) |
2012 ((p_font_style->i_font_alpha & 0xff) << 24),
2013 (p_font_style->i_karaoke_background_color & 0xffffff) |
2014 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2016 if( p_font_style->i_style_flags & STYLE_BOLD )
2018 if( p_font_style->i_style_flags & STYLE_ITALIC )
2020 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2025 rv = PushFont( &p_fonts,
2031 if( rv != VLC_SUCCESS )
2034 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2036 switch ( xml_ReaderNodeType( p_xml_reader ) )
2038 case XML_READER_NONE:
2040 case XML_READER_ENDELEM:
2041 psz_node = xml_ReaderName( p_xml_reader );
2045 if( !strcasecmp( "font", psz_node ) )
2046 PopFont( &p_fonts );
2047 else if( !strcasecmp( "b", psz_node ) )
2049 else if( !strcasecmp( "i", psz_node ) )
2051 else if( !strcasecmp( "u", psz_node ) )
2057 case XML_READER_STARTELEM:
2058 psz_node = xml_ReaderName( p_xml_reader );
2061 if( !strcasecmp( "font", psz_node ) )
2062 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2063 else if( !strcasecmp( "b", psz_node ) )
2065 else if( !strcasecmp( "i", psz_node ) )
2067 else if( !strcasecmp( "u", psz_node ) )
2069 else if( !strcasecmp( "br", psz_node ) )
2071 SetupLine( p_filter, "\n", &psz_text,
2072 pi_runs, ppi_run_lengths, ppp_styles,
2073 GetStyleFromFontStack( p_sys,
2079 else if( !strcasecmp( "k", psz_node ) )
2081 /* Only valid in karaoke */
2084 if( *pi_k_runs > 0 )
2086 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2087 *pi_k_runs, *ppi_k_run_lengths );
2089 SetupKaraoke( p_xml_reader, pi_k_runs,
2090 ppi_k_run_lengths, ppi_k_durations );
2097 case XML_READER_TEXT:
2098 psz_node = xml_ReaderValue( p_xml_reader );
2101 /* Turn any multiple-whitespaces into single spaces */
2102 char *s = strpbrk( psz_node, "\t\r\n " );
2105 int i_whitespace = strspn( s, "\t\r\n " );
2107 if( i_whitespace > 1 )
2110 strlen( s ) - i_whitespace + 1 );
2113 s = strpbrk( s, "\t\r\n " );
2115 SetupLine( p_filter, psz_node, &psz_text,
2116 pi_runs, ppi_run_lengths, ppp_styles,
2117 GetStyleFromFontStack( p_sys,
2126 if( rv != VLC_SUCCESS )
2128 psz_text = psz_text_orig;
2134 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2135 *pi_k_runs, *ppi_k_run_lengths );
2138 *pi_len = psz_text - psz_text_orig;
2140 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2145 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2149 for( k=0; k < p_sys->i_font_attachments; k++ )
2151 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2153 FT_Face p_face = NULL;
2155 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2163 bool match = !strcasecmp( p_face->family_name,
2164 p_style->psz_fontname );
2166 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2167 match = match && p_style->b_bold;
2169 match = match && !p_style->b_bold;
2171 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2172 match = match && p_style->b_italic;
2174 match = match && !p_style->b_italic;
2182 FT_Done_Face( p_face );
2187 return VLC_EGENERIC;
2190 static int CheckIfFontBuildComplete( filter_t *p_filter )
2192 filter_sys_t *p_sys = p_filter->p_sys;
2193 vlc_object_t *p_fb = vlc_object_find_name( p_filter->p_libvlc,
2198 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
2201 if( VLC_SUCCESS == var_Get( p_fb, "build-done", &val ))
2203 p_sys->b_fontconfig_ok = val.b_bool;
2205 if( p_sys->b_fontconfig_ok )
2208 p_sys->p_fontconfig = FcConfigGetCurrent();
2211 msg_Dbg( p_filter, "Font Build still not complete" );
2213 vlc_mutex_unlock( lock );
2214 vlc_object_release( p_fb );
2218 return VLC_EGENERIC;
2221 static int ProcessLines( filter_t *p_filter,
2226 uint32_t *pi_run_lengths,
2227 ft_style_t **pp_styles,
2228 line_desc_t **pp_lines,
2230 FT_Vector *p_result,
2234 uint32_t *pi_k_run_lengths,
2235 uint32_t *pi_k_durations )
2237 filter_sys_t *p_sys = p_filter->p_sys;
2238 ft_style_t **pp_char_styles;
2239 int *p_new_positions = NULL;
2240 int8_t *p_levels = NULL;
2241 uint8_t *pi_karaoke_bar = NULL;
2245 /* Assign each character in the text string its style explicitly, so that
2246 * after the characters have been shuffled around by Fribidi, we can re-apply
2247 * the styles, and to simplify the calculation of runs within a line.
2249 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2250 if( !pp_char_styles )
2255 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2256 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2257 * we just won't be able to display the progress bar; at least we'll
2263 for( j = 0; j < i_runs; j++ )
2264 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2265 pp_char_styles[ i++ ] = pp_styles[ j ];
2267 #if defined(HAVE_FRIBIDI)
2269 ft_style_t **pp_char_styles_new;
2270 int *p_old_positions;
2271 uint32_t *p_fribidi_string;
2272 int start_pos, pos = 0;
2274 pp_char_styles_new = (ft_style_t **)
2275 malloc( i_len * sizeof( ft_style_t * ));
2277 p_fribidi_string = (uint32_t *)
2278 malloc( (i_len + 1) * sizeof(uint32_t) );
2279 p_old_positions = (int *)
2280 malloc( (i_len + 1) * sizeof( int ) );
2281 p_new_positions = (int *)
2282 malloc( (i_len + 1) * sizeof( int ) );
2283 p_levels = (int8_t *)
2284 malloc( (i_len + 1) * sizeof( int8_t ) );
2286 if( ! pp_char_styles_new ||
2287 ! p_fribidi_string ||
2288 ! p_old_positions ||
2289 ! p_new_positions ||
2292 msg_Err( p_filter, "out of memory" );
2294 free( p_old_positions );
2295 free( p_new_positions );
2296 free( p_fribidi_string );
2297 free( pp_char_styles_new );
2298 free( pi_karaoke_bar );
2300 free( pp_char_styles );
2304 /* Do bidi conversion line-by-line */
2307 while(pos < i_len) {
2308 if (psz_text[pos] != '\n')
2310 p_fribidi_string[pos] = psz_text[pos];
2311 pp_char_styles_new[pos] = pp_char_styles[pos];
2312 p_new_positions[pos] = pos;
2317 while(pos < i_len) {
2318 if (psz_text[pos] == '\n')
2322 if (pos > start_pos)
2324 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2325 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2326 pos - start_pos, &base_dir,
2327 (FriBidiChar*)p_fribidi_string + start_pos,
2328 p_new_positions + start_pos,
2330 p_levels + start_pos );
2331 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2333 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2334 p_old_positions[ j - start_pos ] ];
2335 p_new_positions[ j ] += start_pos;
2339 free( p_old_positions );
2340 free( pp_char_styles );
2341 pp_char_styles = pp_char_styles_new;
2342 psz_text = p_fribidi_string;
2343 p_fribidi_string[ i_len ] = 0;
2346 /* Work out the karaoke */
2347 if( pi_karaoke_bar )
2349 int64_t i_last_duration = 0;
2350 int64_t i_duration = 0;
2351 int64_t i_start_pos = 0;
2352 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2354 for( k = 0; k< i_k_runs; k++ )
2356 double fraction = 0.0;
2358 i_duration += pi_k_durations[ k ];
2360 if( i_duration < i_elapsed )
2362 /* Completely finished this run-length -
2363 * let it render normally */
2367 else if( i_elapsed < i_last_duration )
2369 /* Haven't got up to this segment yet -
2370 * render it completely in karaoke BG mode */
2376 /* Partway through this run */
2378 fraction = (double)(i_elapsed - i_last_duration) /
2379 (double)pi_k_durations[ k ];
2381 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2383 double shade = pi_k_run_lengths[ k ] * fraction;
2385 if( p_new_positions )
2386 j = p_new_positions[ i_start_pos + i ];
2388 j = i_start_pos + i;
2390 if( i < (uint32_t)shade )
2391 pi_karaoke_bar[ j ] = 0xff;
2392 else if( (double)i > shade )
2393 pi_karaoke_bar[ j ] = 0x00;
2396 shade -= (int)shade;
2397 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2398 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2402 i_last_duration = i_duration;
2403 i_start_pos += pi_k_run_lengths[ k ];
2407 free( p_new_positions );
2409 FT_Vector tmp_result;
2411 line_desc_t *p_line = NULL;
2412 line_desc_t *p_prev = NULL;
2418 p_result->x = p_result->y = 0;
2419 tmp_result.x = tmp_result.y = 0;
2422 for( k = 0; k <= (uint32_t) i_len; k++ )
2424 if( ( k == (uint32_t) i_len ) ||
2426 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2428 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2430 /* End of the current style run */
2431 FT_Face p_face = NULL;
2434 /* Look for a match amongst our attachments first */
2435 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2437 if( !p_sys->b_fontconfig_ok )
2439 if( VLC_EGENERIC == CheckIfFontBuildComplete( p_filter ))
2440 msg_Err( p_filter, "Can't find FontBuilder thread!" );
2443 if( ! p_face && p_sys->b_fontconfig_ok )
2446 vlc_mutex_lock( &p_sys->fontconfig_lock );
2448 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2449 p_style->psz_fontname,
2453 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2455 if( psz_fontfile && ! *psz_fontfile )
2457 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2458 " so using default font", p_style->psz_fontname,
2459 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2460 (p_style->b_bold ? "(Bold)" :
2461 (p_style->b_italic ? "(Italic)" : ""))) );
2462 free( psz_fontfile );
2463 psz_fontfile = NULL;
2468 if( FT_New_Face( p_sys->p_library,
2469 psz_fontfile, i_idx, &p_face ) )
2471 free( psz_fontfile );
2472 free( pp_char_styles );
2473 #if defined(HAVE_FRIBIDI)
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 free( pi_karaoke_bar );
2503 return VLC_EGENERIC;
2505 p_sys->i_use_kerning =
2506 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2509 uint32_t *psz_unicode = (uint32_t *)
2510 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2513 msg_Err( p_filter, "out of memory" );
2514 if( p_face ) FT_Done_Face( p_face );
2515 free( pp_char_styles );
2516 free( psz_unicode );
2517 #if defined(HAVE_FRIBIDI)
2520 free( pi_karaoke_bar );
2523 memcpy( psz_unicode, psz_text + i_prev,
2524 sizeof( uint32_t ) * ( k - i_prev ) );
2525 psz_unicode[ k - i_prev ] = 0;
2526 while( *psz_unicode )
2530 if( !(p_line = NewLine( i_len - i_prev)) )
2532 msg_Err( p_filter, "out of memory" );
2533 if( p_face ) FT_Done_Face( p_face );
2534 free( pp_char_styles );
2535 free( psz_unicode );
2536 #if defined(HAVE_FRIBIDI)
2539 free( pi_karaoke_bar );
2542 /* New Color mode only works in YUVA rendering mode --
2543 * (RGB mode has palette constraints on it). We therefore
2544 * need to populate the legacy colour fields also.
2546 p_line->b_new_color_mode = true;
2547 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2548 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2549 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2550 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2551 p_line->p_next = NULL;
2553 i_pen_y += tmp_result.y;
2557 if( p_prev ) p_prev->p_next = p_line;
2558 else *pp_lines = p_line;
2561 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2562 p_style->i_font_color, p_style->b_underline,
2563 p_style->i_karaoke_bg_color,
2564 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2565 &tmp_result ) != VLC_SUCCESS )
2567 if( p_face ) FT_Done_Face( p_face );
2568 free( pp_char_styles );
2569 free( psz_unicode );
2570 #if defined(HAVE_FRIBIDI)
2573 free( pi_karaoke_bar );
2574 return VLC_EGENERIC;
2579 p_result->x = __MAX( p_result->x, tmp_result.x );
2580 p_result->y += tmp_result.y;
2585 if( *psz_unicode == '\n')
2589 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2591 *c_ptr = *(c_ptr+1);
2596 free( psz_unicode );
2597 if( p_face ) FT_Done_Face( p_face );
2601 free( pp_char_styles );
2602 #if defined(HAVE_FRIBIDI)
2607 p_result->x = __MAX( p_result->x, tmp_result.x );
2608 p_result->y += tmp_result.y;
2611 if( pi_karaoke_bar )
2614 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2616 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2618 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2622 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2624 /* 100% BG colour will render faster if we
2625 * instead make it 100% FG colour, so leave
2626 * the ratio alone and copy the value across
2628 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2632 if( pi_karaoke_bar[ i ] & 0x80 )
2634 /* Swap Left and Right sides over for Right aligned
2635 * language text (eg. Arabic, Hebrew)
2637 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2639 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2640 p_line->p_bg_rgb[ k ] = i_tmp;
2642 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2645 /* Jump over the '\n' at the line-end */
2648 free( pi_karaoke_bar );
2654 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2655 subpicture_region_t *p_region_in )
2657 int rv = VLC_SUCCESS;
2658 stream_t *p_sub = NULL;
2659 xml_t *p_xml = NULL;
2660 xml_reader_t *p_xml_reader = NULL;
2662 if( !p_region_in || !p_region_in->psz_html )
2663 return VLC_EGENERIC;
2665 /* Reset the default fontsize in case screen metrics have changed */
2666 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2668 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2669 (uint8_t *) p_region_in->psz_html,
2670 strlen( p_region_in->psz_html ),
2674 p_xml = xml_Create( p_filter );
2677 bool b_karaoke = false;
2679 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2682 /* Look for Root Node */
2683 if( xml_ReaderRead( p_xml_reader ) == 1 )
2685 char *psz_node = xml_ReaderName( p_xml_reader );
2687 if( !strcasecmp( "karaoke", psz_node ) )
2689 /* We're going to have to render the text a number
2690 * of times to show the progress marker on the text.
2692 var_SetBool( p_filter, "text-rerender", true );
2695 else if( !strcasecmp( "text", psz_node ) )
2701 /* Only text and karaoke tags are supported */
2702 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2703 xml_ReaderDelete( p_xml, p_xml_reader );
2704 p_xml_reader = NULL;
2716 uint32_t i_runs = 0;
2717 uint32_t i_k_runs = 0;
2718 uint32_t *pi_run_lengths = NULL;
2719 uint32_t *pi_k_run_lengths = NULL;
2720 uint32_t *pi_k_durations = NULL;
2721 ft_style_t **pp_styles = NULL;
2723 line_desc_t *p_lines = NULL;
2725 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2726 sizeof( uint32_t ) );
2731 rv = ProcessNodes( p_filter, p_xml_reader,
2732 p_region_in->p_style, psz_text, &i_len,
2733 &i_runs, &pi_run_lengths, &pp_styles,
2734 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2737 p_region_out->i_x = p_region_in->i_x;
2738 p_region_out->i_y = p_region_in->i_y;
2740 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2742 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2743 pi_run_lengths, pp_styles, &p_lines, &result,
2744 b_karaoke, i_k_runs, pi_k_run_lengths,
2748 for( k=0; k<i_runs; k++)
2749 DeleteStyle( pp_styles[k] );
2751 free( pi_run_lengths );
2754 /* Don't attempt to render text that couldn't be layed out
2757 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2759 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2761 Render( p_filter, p_region_out, p_lines,
2762 result.x, result.y );
2766 RenderYUVA( p_filter, p_region_out, p_lines,
2767 result.x, result.y );
2771 FreeLines( p_lines );
2773 xml_ReaderDelete( p_xml, p_xml_reader );
2775 xml_Delete( p_xml );
2777 stream_Delete( p_sub );
2783 static char* FontConfig_Select( FcConfig* priv, const char* family,
2784 bool b_bold, bool b_italic, int *i_idx )
2787 FcPattern *pat, *p_pat;
2791 pat = FcPatternCreate();
2792 if (!pat) return NULL;
2794 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2795 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2796 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2797 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2799 FcDefaultSubstitute( pat );
2801 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2803 FcPatternDestroy( pat );
2807 p_pat = FcFontMatch( priv, pat, &result );
2808 FcPatternDestroy( pat );
2809 if( !p_pat ) return NULL;
2811 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2812 || ( val_b != FcTrue ) )
2814 FcPatternDestroy( p_pat );
2817 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2822 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2824 FcPatternDestroy( p_pat );
2829 if( strcasecmp((const char*)val_s, family ) != 0 )
2830 msg_Warn( p_filter, "fontconfig: selected font family is not"
2831 "the requested one: '%s' != '%s'\n",
2832 (const char*)val_s, family );
2835 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2837 FcPatternDestroy( p_pat );
2841 FcPatternDestroy( p_pat );
2842 return strdup( (const char*)val_s );
2846 static void FreeLine( line_desc_t *p_line )
2849 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2851 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2853 free( p_line->pp_glyphs );
2854 free( p_line->p_glyph_pos );
2855 free( p_line->p_fg_rgb );
2856 free( p_line->p_bg_rgb );
2857 free( p_line->p_fg_bg_ratio );
2858 free( p_line->pi_underline_offset );
2859 free( p_line->pi_underline_thickness );
2863 static void FreeLines( line_desc_t *p_lines )
2865 line_desc_t *p_line, *p_next;
2867 if( !p_lines ) return;
2869 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2871 p_next = p_line->p_next;
2876 static line_desc_t *NewLine( int i_count )
2878 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2880 if( !p_line ) return NULL;
2881 p_line->i_height = 0;
2882 p_line->i_width = 0;
2883 p_line->p_next = NULL;
2885 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2886 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2887 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2888 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2889 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2890 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2891 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2892 if( ( p_line->pp_glyphs == NULL ) ||
2893 ( p_line->p_glyph_pos == NULL ) ||
2894 ( p_line->p_fg_rgb == NULL ) ||
2895 ( p_line->p_bg_rgb == NULL ) ||
2896 ( p_line->p_fg_bg_ratio == NULL ) ||
2897 ( p_line->pi_underline_offset == NULL ) ||
2898 ( p_line->pi_underline_thickness == NULL ) )
2900 free( p_line->pi_underline_thickness );
2901 free( p_line->pi_underline_offset );
2902 free( p_line->p_fg_rgb );
2903 free( p_line->p_bg_rgb );
2904 free( p_line->p_fg_bg_ratio );
2905 free( p_line->p_glyph_pos );
2906 free( p_line->pp_glyphs );
2910 p_line->pp_glyphs[0] = NULL;
2911 p_line->b_new_color_mode = false;
2916 static int GetFontSize( filter_t *p_filter )
2918 filter_sys_t *p_sys = p_filter->p_sys;
2922 if( p_sys->i_default_font_size )
2924 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2925 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2927 i_size = p_sys->i_default_font_size;
2931 var_Get( p_filter, "freetype-rel-fontsize", &val );
2932 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2933 p_filter->p_sys->i_display_height =
2934 p_filter->fmt_out.video.i_height;
2938 msg_Warn( p_filter, "invalid fontsize, using 12" );
2939 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2940 i_size = 12 * val.i_int / 1000;
2947 static int SetFontSize( filter_t *p_filter, int i_size )
2949 filter_sys_t *p_sys = p_filter->p_sys;
2953 i_size = GetFontSize( p_filter );
2955 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2958 p_sys->i_font_size = i_size;
2960 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2962 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2963 return VLC_EGENERIC;
2969 static void YUVFromRGB( uint32_t i_argb,
2970 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2972 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2973 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2974 int i_blue = ( i_argb & 0x000000ff );
2976 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2977 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2978 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2979 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2980 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2981 -585 * i_blue + 4096 + 1048576) >> 13, 240);