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 );
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, vlc_bool_t 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++ )
768 vlc_bool_t b_ok = VLC_TRUE;
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 if( psz_unicode_orig ) free( psz_unicode_orig );
1362 FreeLines( p_lines );
1366 if( psz_unicode_orig ) 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, vlc_bool_t b_bold,
1374 vlc_bool_t b_italic, vlc_bool_t 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 if( p_style->psz_fontname )
1397 free( p_style->psz_fontname );
1402 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1409 if(( s1->i_font_size == s2->i_font_size ) &&
1410 ( s1->i_font_color == s2->i_font_color ) &&
1411 ( s1->b_italic == s2->b_italic ) &&
1412 ( s1->b_bold == s2->b_bold ) &&
1413 ( s1->b_underline == s2->b_underline ) &&
1414 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1421 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1422 uint32_t i_color, uint32_t i_karaoke_bg_color )
1424 font_stack_t *p_new;
1427 return VLC_EGENERIC;
1429 p_new = malloc( sizeof( font_stack_t ) );
1433 p_new->p_next = NULL;
1436 p_new->psz_name = strdup( psz_name );
1438 p_new->psz_name = NULL;
1440 p_new->i_size = i_size;
1441 p_new->i_color = i_color;
1442 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1450 font_stack_t *p_last;
1452 for( p_last = *p_font;
1454 p_last = p_last->p_next )
1457 p_last->p_next = p_new;
1462 static int PopFont( font_stack_t **p_font )
1464 font_stack_t *p_last, *p_next_to_last;
1466 if( !p_font || !*p_font )
1467 return VLC_EGENERIC;
1469 p_next_to_last = NULL;
1470 for( p_last = *p_font;
1472 p_last = p_last->p_next )
1474 p_next_to_last = p_last;
1477 if( p_next_to_last )
1478 p_next_to_last->p_next = NULL;
1482 free( p_last->psz_name );
1488 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1489 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1491 font_stack_t *p_last;
1493 if( !p_font || !*p_font )
1494 return VLC_EGENERIC;
1496 for( p_last=*p_font;
1498 p_last=p_last->p_next )
1501 *psz_name = p_last->psz_name;
1502 *i_size = p_last->i_size;
1503 *i_color = p_last->i_color;
1504 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1509 static void IconvText( filter_t *p_filter, const char *psz_string,
1510 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1512 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1514 /* If memory hasn't been allocated for our output string, allocate it here
1515 * - the calling function must now be responsible for freeing it.
1517 if( !*ppsz_unicode )
1518 *ppsz_unicode = (uint32_t *)
1519 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1521 /* We don't need to handle a NULL pointer in *ppsz_unicode
1522 * if we are instead testing for a non NULL value like we are here */
1526 #if defined(WORDS_BIGENDIAN)
1527 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1529 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1531 if( iconv_handle != (vlc_iconv_t)-1 )
1533 char *p_in_buffer, *p_out_buffer;
1534 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1535 i_in_bytes = strlen( psz_string );
1536 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1537 i_out_bytes_left = i_out_bytes;
1538 p_in_buffer = (char *) psz_string;
1539 p_out_buffer = (char *) *ppsz_unicode;
1540 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1541 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1543 vlc_iconv_close( iconv_handle );
1547 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1548 "bytes left %u", (unsigned)i_in_bytes );
1552 *(uint32_t*)p_out_buffer = 0;
1554 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1559 msg_Warn( p_filter, "unable to do conversion" );
1564 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1565 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1566 vlc_bool_t b_uline )
1568 ft_style_t *p_style = NULL;
1570 char *psz_fontname = NULL;
1571 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1572 uint32_t i_karaoke_bg_color = i_font_color;
1573 int i_font_size = p_sys->i_font_size;
1575 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1576 &i_font_color, &i_karaoke_bg_color ))
1578 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1579 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1584 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1585 vlc_bool_t b_uline, int i_karaoke_bgcolor,
1586 line_desc_t *p_line, uint32_t *psz_unicode,
1587 int *pi_pen_x, int i_pen_y, int *pi_start,
1588 FT_Vector *p_result )
1593 vlc_bool_t b_first_on_line = VLC_TRUE;
1596 int i_pen_x_start = *pi_pen_x;
1598 uint32_t *psz_unicode_start = psz_unicode;
1600 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1602 /* Account for part of line already in position */
1603 for( i=0; i<*pi_start; i++ )
1607 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1608 ft_glyph_bbox_pixels, &glyph_size );
1610 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1611 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1612 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1613 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1619 b_first_on_line = VLC_FALSE;
1621 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1627 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1628 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1632 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1633 ft_kerning_default, &delta );
1634 *pi_pen_x += delta.x >> 6;
1636 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1637 p_line->p_glyph_pos[ i ].y = i_pen_y;
1639 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1643 "unable to render text FT_Load_Glyph returned %d", i_error );
1644 p_line->pp_glyphs[ i ] = NULL;
1645 return VLC_EGENERIC;
1647 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1651 "unable to render text FT_Get_Glyph returned %d", i_error );
1652 p_line->pp_glyphs[ i ] = NULL;
1653 return VLC_EGENERIC;
1655 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1656 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1659 FT_Done_Glyph( tmp_glyph );
1664 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1665 p_face->size->metrics.y_scale));
1666 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1667 p_face->size->metrics.y_scale));
1669 p_line->pi_underline_offset[ i ] =
1670 ( aOffset < 0 ) ? -aOffset : aOffset;
1671 p_line->pi_underline_thickness[ i ] =
1672 ( aSize < 0 ) ? -aSize : aSize;
1674 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1675 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1676 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1677 p_line->p_fg_bg_ratio[ i ] = 0x00;
1679 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1680 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1681 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1683 while( --i > *pi_start )
1685 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1688 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1692 if( psz_unicode == psz_unicode_start )
1694 if( b_first_on_line )
1696 msg_Warn( p_filter, "unbreakable string" );
1697 p_line->pp_glyphs[ i ] = NULL;
1698 return VLC_EGENERIC;
1700 *pi_pen_x = i_pen_x_start;
1702 p_line->i_width = line.xMax;
1703 p_line->i_height = __MAX( p_line->i_height,
1704 p_face->size->metrics.height >> 6 );
1705 p_line->pp_glyphs[ i ] = NULL;
1707 p_result->x = __MAX( p_result->x, line.xMax );
1708 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1709 i_yMax - i_yMin ) );
1716 *psz_unicode = '\n';
1718 psz_unicode = psz_unicode_start;
1719 *pi_pen_x = i_pen_x_start;
1727 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1728 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1730 i_previous = i_glyph_index;
1731 *pi_pen_x += p_face->glyph->advance.x >> 6;
1734 p_line->i_width = line.xMax;
1735 p_line->i_height = __MAX( p_line->i_height,
1736 p_face->size->metrics.height >> 6 );
1737 p_line->pp_glyphs[ i ] = NULL;
1739 p_result->x = __MAX( p_result->x, line.xMax );
1740 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1741 line.yMax - line.yMin ) );
1745 /* Get rid of any text processed - if necessary repositioning
1746 * at the start of a new line of text
1750 *psz_unicode_start = '\0';
1752 else if( psz_unicode > psz_unicode_start )
1754 for( i=0; psz_unicode[ i ]; i++ )
1755 psz_unicode_start[ i ] = psz_unicode[ i ];
1756 psz_unicode_start[ i ] = '\0';
1762 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1763 font_stack_t **p_fonts, int i_scale )
1766 char *psz_fontname = NULL;
1767 uint32_t i_font_color = 0xffffff;
1768 int i_font_alpha = 0;
1769 uint32_t i_karaoke_bg_color = 0x00ffffff;
1770 int i_font_size = 24;
1772 /* Default all attributes to the top font in the stack -- in case not
1773 * all attributes are specified in the sub-font
1775 if( VLC_SUCCESS == PeekFont( p_fonts,
1779 &i_karaoke_bg_color ))
1781 psz_fontname = strdup( psz_fontname );
1782 i_font_size = i_font_size * 1000 / i_scale;
1784 i_font_alpha = (i_font_color >> 24) & 0xff;
1785 i_font_color &= 0x00ffffff;
1787 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1789 char *psz_name = xml_ReaderName( p_xml_reader );
1790 char *psz_value = xml_ReaderValue( p_xml_reader );
1792 if( psz_name && psz_value )
1794 if( !strcasecmp( "face", psz_name ) )
1796 if( psz_fontname ) free( psz_fontname );
1797 psz_fontname = strdup( psz_value );
1799 else if( !strcasecmp( "size", psz_name ) )
1801 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1803 int i_value = atoi( psz_value );
1805 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1806 i_font_size += ( i_value * i_font_size ) / 10;
1807 else if( i_value < -5 )
1808 i_font_size = - i_value;
1809 else if( i_value > 5 )
1810 i_font_size = i_value;
1813 i_font_size = atoi( psz_value );
1815 else if( !strcasecmp( "color", psz_name ) &&
1816 ( psz_value[0] == '#' ) )
1818 i_font_color = strtol( psz_value + 1, NULL, 16 );
1819 i_font_color &= 0x00ffffff;
1821 else if( !strcasecmp( "alpha", psz_name ) &&
1822 ( psz_value[0] == '#' ) )
1824 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1825 i_font_alpha &= 0xff;
1831 rv = PushFont( p_fonts,
1833 i_font_size * i_scale / 1000,
1834 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1835 i_karaoke_bg_color );
1837 free( psz_fontname );
1842 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1843 uint32_t **psz_text_out, uint32_t *pi_runs,
1844 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1845 ft_style_t *p_style )
1847 uint32_t i_string_length = 0;
1849 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1850 *psz_text_out += i_string_length;
1852 if( ppp_styles && ppi_run_lengths )
1858 *ppp_styles = (ft_style_t **)
1859 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1861 else if( *pi_runs == 1 )
1863 *ppp_styles = (ft_style_t **)
1864 malloc( *pi_runs * sizeof( ft_style_t * ) );
1867 /* We have just malloc'ed this memory successfully -
1868 * *pi_runs HAS to be within the memory area of *ppp_styles */
1871 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1875 if( *ppi_run_lengths )
1877 *ppi_run_lengths = (uint32_t *)
1878 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1880 else if( *pi_runs == 1 )
1882 *ppi_run_lengths = (uint32_t *)
1883 malloc( *pi_runs * sizeof( uint32_t ) );
1886 /* same remarks here */
1887 if( *ppi_run_lengths )
1889 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1892 /* If we couldn't use the p_style argument due to memory allocation
1893 * problems above, release it here.
1895 if( p_style ) DeleteStyle( p_style );
1898 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1899 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1901 /* Karaoke tags _PRECEDE_ the text they specify a duration
1902 * for, therefore we are working out the length for the
1903 * previous tag, and first time through we have nothing
1905 if( pi_k_run_lengths )
1910 /* Work out how many characters are presently in the string
1912 for( i = 0; i < i_runs; i++ )
1913 i_chars += pi_run_lengths[ i ];
1915 /* Subtract away those we've already allocated to other
1918 for( i = 0; i < i_k_runs; i++ )
1919 i_chars -= pi_k_run_lengths[ i ];
1921 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1925 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1926 uint32_t **ppi_k_run_lengths,
1927 uint32_t **ppi_k_durations )
1929 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1931 char *psz_name = xml_ReaderName( p_xml_reader );
1932 char *psz_value = xml_ReaderValue( p_xml_reader );
1934 if( psz_name && psz_value &&
1935 !strcasecmp( "t", psz_name ) )
1937 if( ppi_k_durations && ppi_k_run_lengths )
1941 if( *ppi_k_durations )
1943 *ppi_k_durations = (uint32_t *)
1944 realloc( *ppi_k_durations,
1945 *pi_k_runs * sizeof( uint32_t ) );
1947 else if( *pi_k_runs == 1 )
1949 *ppi_k_durations = (uint32_t *)
1950 malloc( *pi_k_runs * sizeof( uint32_t ) );
1953 if( *ppi_k_run_lengths )
1955 *ppi_k_run_lengths = (uint32_t *)
1956 realloc( *ppi_k_run_lengths,
1957 *pi_k_runs * sizeof( uint32_t ) );
1959 else if( *pi_k_runs == 1 )
1961 *ppi_k_run_lengths = (uint32_t *)
1962 malloc( *pi_k_runs * sizeof( uint32_t ) );
1964 if( *ppi_k_durations )
1965 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1967 if( *ppi_k_run_lengths )
1968 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1971 if( psz_name ) free( psz_name );
1972 if( psz_value ) free( psz_value );
1976 static int ProcessNodes( filter_t *p_filter,
1977 xml_reader_t *p_xml_reader,
1978 text_style_t *p_font_style,
1983 uint32_t **ppi_run_lengths,
1984 ft_style_t ***ppp_styles,
1986 vlc_bool_t b_karaoke,
1987 uint32_t *pi_k_runs,
1988 uint32_t **ppi_k_run_lengths,
1989 uint32_t **ppi_k_durations )
1991 int rv = VLC_SUCCESS;
1992 filter_sys_t *p_sys = p_filter->p_sys;
1993 uint32_t *psz_text_orig = psz_text;
1994 font_stack_t *p_fonts = NULL;
1998 char *psz_node = NULL;
2000 vlc_bool_t b_italic = VLC_FALSE;
2001 vlc_bool_t b_bold = VLC_FALSE;
2002 vlc_bool_t b_uline = VLC_FALSE;
2004 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2005 i_scale = val.i_int;
2009 rv = PushFont( &p_fonts,
2010 p_font_style->psz_fontname,
2011 p_font_style->i_font_size * i_scale / 1000,
2012 (p_font_style->i_font_color & 0xffffff) |
2013 ((p_font_style->i_font_alpha & 0xff) << 24),
2014 (p_font_style->i_karaoke_background_color & 0xffffff) |
2015 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2017 if( p_font_style->i_style_flags & STYLE_BOLD )
2019 if( p_font_style->i_style_flags & STYLE_ITALIC )
2020 b_italic = VLC_TRUE;
2021 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2026 rv = PushFont( &p_fonts,
2032 if( rv != VLC_SUCCESS )
2035 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2037 switch ( xml_ReaderNodeType( p_xml_reader ) )
2039 case XML_READER_NONE:
2041 case XML_READER_ENDELEM:
2042 psz_node = xml_ReaderName( p_xml_reader );
2046 if( !strcasecmp( "font", psz_node ) )
2047 PopFont( &p_fonts );
2048 else if( !strcasecmp( "b", psz_node ) )
2050 else if( !strcasecmp( "i", psz_node ) )
2051 b_italic = VLC_FALSE;
2052 else if( !strcasecmp( "u", psz_node ) )
2053 b_uline = VLC_FALSE;
2058 case XML_READER_STARTELEM:
2059 psz_node = xml_ReaderName( p_xml_reader );
2062 if( !strcasecmp( "font", psz_node ) )
2063 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2064 else if( !strcasecmp( "b", psz_node ) )
2066 else if( !strcasecmp( "i", psz_node ) )
2067 b_italic = VLC_TRUE;
2068 else if( !strcasecmp( "u", psz_node ) )
2070 else if( !strcasecmp( "br", psz_node ) )
2072 SetupLine( p_filter, "\n", &psz_text,
2073 pi_runs, ppi_run_lengths, ppp_styles,
2074 GetStyleFromFontStack( p_sys,
2080 else if( !strcasecmp( "k", psz_node ) )
2082 /* Only valid in karaoke */
2085 if( *pi_k_runs > 0 )
2087 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2088 *pi_k_runs, *ppi_k_run_lengths );
2090 SetupKaraoke( p_xml_reader, pi_k_runs,
2091 ppi_k_run_lengths, ppi_k_durations );
2098 case XML_READER_TEXT:
2099 psz_node = xml_ReaderValue( p_xml_reader );
2102 /* Turn any multiple-whitespaces into single spaces */
2103 char *s = strpbrk( psz_node, "\t\r\n " );
2106 int i_whitespace = strspn( s, "\t\r\n " );
2108 if( i_whitespace > 1 )
2111 strlen( s ) - i_whitespace + 1 );
2114 s = strpbrk( s, "\t\r\n " );
2116 SetupLine( p_filter, psz_node, &psz_text,
2117 pi_runs, ppi_run_lengths, ppp_styles,
2118 GetStyleFromFontStack( p_sys,
2127 if( rv != VLC_SUCCESS )
2129 psz_text = psz_text_orig;
2135 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2136 *pi_k_runs, *ppi_k_run_lengths );
2139 *pi_len = psz_text - psz_text_orig;
2141 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2146 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2150 for( k=0; k < p_sys->i_font_attachments; k++ )
2152 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2154 FT_Face p_face = NULL;
2156 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2164 vlc_bool_t match = !strcasecmp( p_face->family_name,
2165 p_style->psz_fontname );
2167 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2168 match = match && p_style->b_bold;
2170 match = match && !p_style->b_bold;
2172 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2173 match = match && p_style->b_italic;
2175 match = match && !p_style->b_italic;
2183 FT_Done_Face( p_face );
2188 return VLC_EGENERIC;
2191 static int CheckIfFontBuildComplete( filter_t *p_filter )
2193 filter_sys_t *p_sys = p_filter->p_sys;
2194 vlc_object_t *p_fb = vlc_object_find_name( p_filter->p_libvlc,
2199 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
2202 if( VLC_SUCCESS == var_Get( p_fb, "build-done", &val ))
2204 p_sys->b_fontconfig_ok = val.b_bool;
2206 if( p_sys->b_fontconfig_ok )
2209 p_sys->p_fontconfig = FcConfigGetCurrent();
2212 msg_Dbg( p_filter, "Font Build still not complete" );
2214 vlc_mutex_unlock( lock );
2215 vlc_object_release( p_fb );
2219 return VLC_EGENERIC;
2222 static int ProcessLines( filter_t *p_filter,
2227 uint32_t *pi_run_lengths,
2228 ft_style_t **pp_styles,
2229 line_desc_t **pp_lines,
2231 FT_Vector *p_result,
2233 vlc_bool_t b_karaoke,
2235 uint32_t *pi_k_run_lengths,
2236 uint32_t *pi_k_durations )
2238 filter_sys_t *p_sys = p_filter->p_sys;
2239 ft_style_t **pp_char_styles;
2240 int *p_new_positions = NULL;
2241 int8_t *p_levels = NULL;
2242 uint8_t *pi_karaoke_bar = NULL;
2246 /* Assign each character in the text string its style explicitly, so that
2247 * after the characters have been shuffled around by Fribidi, we can re-apply
2248 * the styles, and to simplify the calculation of runs within a line.
2250 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2251 if( !pp_char_styles )
2256 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2257 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2258 * we just won't be able to display the progress bar; at least we'll
2264 for( j = 0; j < i_runs; j++ )
2265 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2266 pp_char_styles[ i++ ] = pp_styles[ j ];
2268 #if defined(HAVE_FRIBIDI)
2270 ft_style_t **pp_char_styles_new;
2271 int *p_old_positions;
2272 uint32_t *p_fribidi_string;
2273 int start_pos, pos = 0;
2275 pp_char_styles_new = (ft_style_t **)
2276 malloc( i_len * sizeof( ft_style_t * ));
2278 p_fribidi_string = (uint32_t *)
2279 malloc( (i_len + 1) * sizeof(uint32_t) );
2280 p_old_positions = (int *)
2281 malloc( (i_len + 1) * sizeof( int ) );
2282 p_new_positions = (int *)
2283 malloc( (i_len + 1) * sizeof( int ) );
2284 p_levels = (int8_t *)
2285 malloc( (i_len + 1) * sizeof( int8_t ) );
2287 if( ! pp_char_styles_new ||
2288 ! p_fribidi_string ||
2289 ! p_old_positions ||
2290 ! p_new_positions ||
2293 msg_Err( p_filter, "out of memory" );
2294 if( p_levels ) free( p_levels );
2295 if( p_old_positions ) free( p_old_positions );
2296 if( p_new_positions ) free( p_new_positions );
2297 if( p_fribidi_string ) free( p_fribidi_string );
2298 if( pp_char_styles_new ) free( pp_char_styles_new );
2299 if( pi_karaoke_bar ) free( pi_karaoke_bar );
2301 free( pp_char_styles );
2305 /* Do bidi conversion line-by-line */
2308 while(pos < i_len) {
2309 if (psz_text[pos] != '\n')
2311 p_fribidi_string[pos] = psz_text[pos];
2312 pp_char_styles_new[pos] = pp_char_styles[pos];
2313 p_new_positions[pos] = pos;
2318 while(pos < i_len) {
2319 if (psz_text[pos] == '\n')
2323 if (pos > start_pos)
2325 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2326 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2327 pos - start_pos, &base_dir,
2328 (FriBidiChar*)p_fribidi_string + start_pos,
2329 p_new_positions + start_pos,
2331 p_levels + start_pos );
2332 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2334 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2335 p_old_positions[ j - start_pos ] ];
2336 p_new_positions[ j ] += start_pos;
2340 free( p_old_positions );
2341 free( pp_char_styles );
2342 pp_char_styles = pp_char_styles_new;
2343 psz_text = p_fribidi_string;
2344 p_fribidi_string[ i_len ] = 0;
2347 /* Work out the karaoke */
2348 if( pi_karaoke_bar )
2350 int64_t i_last_duration = 0;
2351 int64_t i_duration = 0;
2352 int64_t i_start_pos = 0;
2353 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2355 for( k = 0; k< i_k_runs; k++ )
2357 double fraction = 0.0;
2359 i_duration += pi_k_durations[ k ];
2361 if( i_duration < i_elapsed )
2363 /* Completely finished this run-length -
2364 * let it render normally */
2368 else if( i_elapsed < i_last_duration )
2370 /* Haven't got up to this segment yet -
2371 * render it completely in karaoke BG mode */
2377 /* Partway through this run */
2379 fraction = (double)(i_elapsed - i_last_duration) /
2380 (double)pi_k_durations[ k ];
2382 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2384 double shade = pi_k_run_lengths[ k ] * fraction;
2386 if( p_new_positions )
2387 j = p_new_positions[ i_start_pos + i ];
2389 j = i_start_pos + i;
2391 if( i < (uint32_t)shade )
2392 pi_karaoke_bar[ j ] = 0xff;
2393 else if( (double)i > shade )
2394 pi_karaoke_bar[ j ] = 0x00;
2397 shade -= (int)shade;
2398 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2399 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2403 i_last_duration = i_duration;
2404 i_start_pos += pi_k_run_lengths[ k ];
2407 if( p_levels ) free( p_levels );
2408 if( p_new_positions ) free( p_new_positions );
2410 FT_Vector tmp_result;
2412 line_desc_t *p_line = NULL;
2413 line_desc_t *p_prev = NULL;
2419 p_result->x = p_result->y = 0;
2420 tmp_result.x = tmp_result.y = 0;
2423 for( k = 0; k <= (uint32_t) i_len; k++ )
2425 if( ( k == (uint32_t) i_len ) ||
2427 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2429 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2431 /* End of the current style run */
2432 FT_Face p_face = NULL;
2435 /* Look for a match amongst our attachments first */
2436 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2438 if( !p_sys->b_fontconfig_ok )
2440 if( VLC_EGENERIC == CheckIfFontBuildComplete( p_filter ))
2441 msg_Err( p_filter, "Can't find FontBuilder thread!" );
2444 if( ! p_face && p_sys->b_fontconfig_ok )
2447 vlc_mutex_lock( &p_sys->fontconfig_lock );
2449 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2450 p_style->psz_fontname,
2454 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2456 if( psz_fontfile && ! *psz_fontfile )
2458 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2459 " so using default font", p_style->psz_fontname,
2460 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2461 (p_style->b_bold ? "(Bold)" :
2462 (p_style->b_italic ? "(Italic)" : ""))) );
2463 free( psz_fontfile );
2464 psz_fontfile = NULL;
2469 if( FT_New_Face( p_sys->p_library,
2470 psz_fontfile, i_idx, &p_face ) )
2472 free( psz_fontfile );
2473 free( pp_char_styles );
2474 #if defined(HAVE_FRIBIDI)
2477 if( pi_karaoke_bar )
2478 free( pi_karaoke_bar );
2479 return VLC_EGENERIC;
2481 free( psz_fontfile );
2485 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2487 /* We've loaded a font face which is unhelpful for actually
2488 * rendering text - fallback to the default one.
2490 FT_Done_Face( p_face );
2494 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2495 ft_encoding_unicode ) ||
2496 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2497 p_style->i_font_size ) )
2499 if( p_face ) FT_Done_Face( p_face );
2500 free( pp_char_styles );
2501 #if defined(HAVE_FRIBIDI)
2504 if( pi_karaoke_bar )
2505 free( pi_karaoke_bar );
2506 return VLC_EGENERIC;
2508 p_sys->i_use_kerning =
2509 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2512 uint32_t *psz_unicode = (uint32_t *)
2513 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2516 msg_Err( p_filter, "out of memory" );
2517 if( p_face ) FT_Done_Face( p_face );
2518 free( pp_char_styles );
2519 free( psz_unicode );
2520 #if defined(HAVE_FRIBIDI)
2523 if( pi_karaoke_bar )
2524 free( pi_karaoke_bar );
2527 memcpy( psz_unicode, psz_text + i_prev,
2528 sizeof( uint32_t ) * ( k - i_prev ) );
2529 psz_unicode[ k - i_prev ] = 0;
2530 while( *psz_unicode )
2534 if( !(p_line = NewLine( i_len - i_prev)) )
2536 msg_Err( p_filter, "out of memory" );
2537 if( p_face ) FT_Done_Face( p_face );
2538 free( pp_char_styles );
2539 free( psz_unicode );
2540 #if defined(HAVE_FRIBIDI)
2543 if( pi_karaoke_bar )
2544 free( pi_karaoke_bar );
2547 /* New Color mode only works in YUVA rendering mode --
2548 * (RGB mode has palette constraints on it). We therefore
2549 * need to populate the legacy colour fields also.
2551 p_line->b_new_color_mode = VLC_TRUE;
2552 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2553 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2554 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2555 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2556 p_line->p_next = NULL;
2558 i_pen_y += tmp_result.y;
2562 if( p_prev ) p_prev->p_next = p_line;
2563 else *pp_lines = p_line;
2566 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2567 p_style->i_font_color, p_style->b_underline,
2568 p_style->i_karaoke_bg_color,
2569 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2570 &tmp_result ) != VLC_SUCCESS )
2572 if( p_face ) FT_Done_Face( p_face );
2573 free( pp_char_styles );
2574 free( psz_unicode );
2575 #if defined(HAVE_FRIBIDI)
2578 if( pi_karaoke_bar )
2579 free( pi_karaoke_bar );
2580 return VLC_EGENERIC;
2585 p_result->x = __MAX( p_result->x, tmp_result.x );
2586 p_result->y += tmp_result.y;
2591 if( *psz_unicode == '\n')
2595 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2597 *c_ptr = *(c_ptr+1);
2602 free( psz_unicode );
2603 if( p_face ) FT_Done_Face( p_face );
2607 free( pp_char_styles );
2608 #if defined(HAVE_FRIBIDI)
2613 p_result->x = __MAX( p_result->x, tmp_result.x );
2614 p_result->y += tmp_result.y;
2617 if( pi_karaoke_bar )
2620 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2622 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2624 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2628 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2630 /* 100% BG colour will render faster if we
2631 * instead make it 100% FG colour, so leave
2632 * the ratio alone and copy the value across
2634 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2638 if( pi_karaoke_bar[ i ] & 0x80 )
2640 /* Swap Left and Right sides over for Right aligned
2641 * language text (eg. Arabic, Hebrew)
2643 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2645 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2646 p_line->p_bg_rgb[ k ] = i_tmp;
2648 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2651 /* Jump over the '\n' at the line-end */
2654 free( pi_karaoke_bar );
2660 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2661 subpicture_region_t *p_region_in )
2663 int rv = VLC_SUCCESS;
2664 stream_t *p_sub = NULL;
2665 xml_t *p_xml = NULL;
2666 xml_reader_t *p_xml_reader = NULL;
2668 if( !p_region_in || !p_region_in->psz_html )
2669 return VLC_EGENERIC;
2671 /* Reset the default fontsize in case screen metrics have changed */
2672 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2674 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2675 (uint8_t *) p_region_in->psz_html,
2676 strlen( p_region_in->psz_html ),
2680 p_xml = xml_Create( p_filter );
2683 vlc_bool_t b_karaoke = VLC_FALSE;
2685 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2688 /* Look for Root Node */
2689 if( xml_ReaderRead( p_xml_reader ) == 1 )
2691 char *psz_node = xml_ReaderName( p_xml_reader );
2693 if( !strcasecmp( "karaoke", psz_node ) )
2695 /* We're going to have to render the text a number
2696 * of times to show the progress marker on the text.
2698 var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2699 b_karaoke = VLC_TRUE;
2701 else if( !strcasecmp( "text", psz_node ) )
2703 b_karaoke = VLC_FALSE;
2707 /* Only text and karaoke tags are supported */
2708 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2709 xml_ReaderDelete( p_xml, p_xml_reader );
2710 p_xml_reader = NULL;
2722 uint32_t i_runs = 0;
2723 uint32_t i_k_runs = 0;
2724 uint32_t *pi_run_lengths = NULL;
2725 uint32_t *pi_k_run_lengths = NULL;
2726 uint32_t *pi_k_durations = NULL;
2727 ft_style_t **pp_styles = NULL;
2729 line_desc_t *p_lines = NULL;
2731 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2732 sizeof( uint32_t ) );
2737 rv = ProcessNodes( p_filter, p_xml_reader,
2738 p_region_in->p_style, psz_text, &i_len,
2739 &i_runs, &pi_run_lengths, &pp_styles,
2740 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2743 p_region_out->i_x = p_region_in->i_x;
2744 p_region_out->i_y = p_region_in->i_y;
2746 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2748 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2749 pi_run_lengths, pp_styles, &p_lines, &result,
2750 b_karaoke, i_k_runs, pi_k_run_lengths,
2754 for( k=0; k<i_runs; k++)
2755 DeleteStyle( pp_styles[k] );
2757 free( pi_run_lengths );
2760 /* Don't attempt to render text that couldn't be layed out
2763 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2765 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2767 Render( p_filter, p_region_out, p_lines,
2768 result.x, result.y );
2772 RenderYUVA( p_filter, p_region_out, p_lines,
2773 result.x, result.y );
2777 FreeLines( p_lines );
2779 xml_ReaderDelete( p_xml, p_xml_reader );
2781 xml_Delete( p_xml );
2783 stream_Delete( p_sub );
2789 static char* FontConfig_Select( FcConfig* priv, const char* family,
2790 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2793 FcPattern *pat, *p_pat;
2797 pat = FcPatternCreate();
2798 if (!pat) return NULL;
2800 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2801 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2802 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2803 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2805 FcDefaultSubstitute( pat );
2807 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2809 FcPatternDestroy( pat );
2813 p_pat = FcFontMatch( priv, pat, &result );
2814 FcPatternDestroy( pat );
2815 if( !p_pat ) return NULL;
2817 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2818 || ( val_b != FcTrue ) )
2820 FcPatternDestroy( p_pat );
2823 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2828 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2830 FcPatternDestroy( p_pat );
2835 if( strcasecmp((const char*)val_s, family ) != 0 )
2836 msg_Warn( p_filter, "fontconfig: selected font family is not"
2837 "the requested one: '%s' != '%s'\n",
2838 (const char*)val_s, family );
2841 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2843 FcPatternDestroy( p_pat );
2847 FcPatternDestroy( p_pat );
2848 return strdup( (const char*)val_s );
2852 static void FreeLine( line_desc_t *p_line )
2855 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2857 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2859 free( p_line->pp_glyphs );
2860 free( p_line->p_glyph_pos );
2861 free( p_line->p_fg_rgb );
2862 free( p_line->p_bg_rgb );
2863 free( p_line->p_fg_bg_ratio );
2864 free( p_line->pi_underline_offset );
2865 free( p_line->pi_underline_thickness );
2869 static void FreeLines( line_desc_t *p_lines )
2871 line_desc_t *p_line, *p_next;
2873 if( !p_lines ) return;
2875 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2877 p_next = p_line->p_next;
2882 static line_desc_t *NewLine( int i_count )
2884 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2886 if( !p_line ) return NULL;
2887 p_line->i_height = 0;
2888 p_line->i_width = 0;
2889 p_line->p_next = NULL;
2891 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2892 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2893 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2894 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2895 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2896 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2897 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2898 if( ( p_line->pp_glyphs == NULL ) ||
2899 ( p_line->p_glyph_pos == NULL ) ||
2900 ( p_line->p_fg_rgb == NULL ) ||
2901 ( p_line->p_bg_rgb == NULL ) ||
2902 ( p_line->p_fg_bg_ratio == NULL ) ||
2903 ( p_line->pi_underline_offset == NULL ) ||
2904 ( p_line->pi_underline_thickness == NULL ) )
2906 if( p_line->pi_underline_thickness )
2907 free( p_line->pi_underline_thickness );
2908 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2909 if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2910 if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2911 if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2912 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2913 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2917 p_line->pp_glyphs[0] = NULL;
2918 p_line->b_new_color_mode = VLC_FALSE;
2923 static int GetFontSize( filter_t *p_filter )
2925 filter_sys_t *p_sys = p_filter->p_sys;
2929 if( p_sys->i_default_font_size )
2931 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2932 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2934 i_size = p_sys->i_default_font_size;
2938 var_Get( p_filter, "freetype-rel-fontsize", &val );
2939 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2940 p_filter->p_sys->i_display_height =
2941 p_filter->fmt_out.video.i_height;
2945 msg_Warn( p_filter, "invalid fontsize, using 12" );
2946 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2947 i_size = 12 * val.i_int / 1000;
2954 static int SetFontSize( filter_t *p_filter, int i_size )
2956 filter_sys_t *p_sys = p_filter->p_sys;
2960 i_size = GetFontSize( p_filter );
2962 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2965 p_sys->i_font_size = i_size;
2967 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2969 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2970 return VLC_EGENERIC;
2976 static void YUVFromRGB( uint32_t i_argb,
2977 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2979 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2980 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2981 int i_blue = ( i_argb & 0x000000ff );
2983 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2984 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2985 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2986 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2987 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2988 -585 * i_blue + 4096 + 1048576) >> 13, 240);