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 *****************************************************************************/
35 #include <vlc_plugin.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
42 #include <vlc_input.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
52 #define FT_MulFix(v, s) (((v)*(s))>>16)
55 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
56 #define FC_DEFAULT_FONT "Lucida Grande"
57 #elif defined( SYS_BEOS )
58 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
59 #define FC_DEFAULT_FONT "Swiss"
60 #elif defined( WIN32 )
61 #define DEFAULT_FONT "" /* Default font found at run-time */
62 #define FC_DEFAULT_FONT "Arial"
64 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
65 #define FC_DEFAULT_FONT "Serif Bold"
68 #if defined(HAVE_FRIBIDI)
69 #include <fribidi/fribidi.h>
72 #ifdef HAVE_FONTCONFIG
73 #include <fontconfig/fontconfig.h>
76 typedef struct line_desc_t line_desc_t;
78 /*****************************************************************************
80 *****************************************************************************/
81 static int Create ( vlc_object_t * );
82 static void Destroy( vlc_object_t * );
84 static int LoadFontsFromAttachments( filter_t *p_filter );
86 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
87 static int RenderText( filter_t *, subpicture_region_t *,
88 subpicture_region_t * );
89 #ifdef HAVE_FONTCONFIG
90 static int RenderHtml( filter_t *, subpicture_region_t *,
91 subpicture_region_t * );
92 static char *FontConfig_Select( FcConfig *, const char *,
94 static int BuildDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
97 static line_desc_t *NewLine( int );
99 static int GetFontSize( filter_t *p_filter );
100 static int SetFontSize( filter_t *, int );
101 static void YUVFromRGB( uint32_t i_argb,
102 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
104 /*****************************************************************************
106 *****************************************************************************/
107 #define FONT_TEXT N_("Font")
108 #define FONT_LONGTEXT N_("Filename for the font you want to use")
109 #define FONTSIZE_TEXT N_("Font size in pixels")
110 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
111 "that will be rendered on the video. " \
112 "If set to something different than 0 this option will override the " \
113 "relative font size." )
114 #define OPACITY_TEXT N_("Opacity")
115 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
116 "text that will be rendered on the video. 0 = transparent, " \
117 "255 = totally opaque. " )
118 #define COLOR_TEXT N_("Text default color")
119 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
120 "the video. This must be an hexadecimal (like HTML colors). The first two "\
121 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
122 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
123 #define FONTSIZER_TEXT N_("Relative font size")
124 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
125 "fonts that will be rendered on the video. If absolute font size is set, "\
126 "relative size will be overriden." )
128 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
129 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
130 N_("Large"), N_("Larger") };
131 #define YUVP_TEXT N_("Use YUVP renderer")
132 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
133 "This option is only needed if you want to encode into DVB subtitles" )
134 #define EFFECT_TEXT N_("Font Effect")
135 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
136 "text to improve its readability." )
138 #define EFFECT_BACKGROUND 1
139 #define EFFECT_OUTLINE 2
140 #define EFFECT_OUTLINE_FAT 3
142 static int pi_effects[] = { 1, 2, 3 };
143 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
145 static int pi_color_values[] = {
146 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
147 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
148 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
150 static const char *ppsz_color_descriptions[] = {
151 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
152 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
153 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
156 set_shortname( N_("Text renderer"));
157 set_description( N_("Freetype2 font renderer") );
158 set_category( CAT_VIDEO );
159 set_subcategory( SUBCAT_VIDEO_SUBPIC );
161 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
164 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
165 FONTSIZE_LONGTEXT, true );
167 /* opacity valid on 0..255, with default 255 = fully opaque */
168 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
169 OPACITY_TEXT, OPACITY_LONGTEXT, true );
171 /* hook to the color values list, with default 0x00ffffff = white */
172 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
173 COLOR_LONGTEXT, false );
174 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
176 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
177 FONTSIZER_LONGTEXT, false );
178 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
179 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
180 EFFECT_LONGTEXT, false );
181 change_integer_list( pi_effects, ppsz_effects_text, 0 );
183 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
184 YUVP_LONGTEXT, true );
185 set_capability( "text renderer", 100 );
186 add_shortcut( "text" );
187 set_callbacks( Create, Destroy );
192 /** NULL-terminated list of glyphs making the string */
193 FT_BitmapGlyph *pp_glyphs;
194 /** list of relative positions for the glyphs */
195 FT_Vector *p_glyph_pos;
196 /** list of RGB information for styled text
197 * -- if the rendering mode supports it (RenderYUVA) and
198 * b_new_color_mode is set, then it becomes possible to
199 * have multicoloured text within the subtitles. */
202 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
203 bool b_new_color_mode;
204 /** underline information -- only supplied if text should be underlined */
205 uint16_t *pi_underline_offset;
206 uint16_t *pi_underline_thickness;
210 int i_red, i_green, i_blue;
216 typedef struct font_stack_t font_stack_t;
221 uint32_t i_color; /* ARGB */
222 uint32_t i_karaoke_bg_color; /* ARGB */
224 font_stack_t *p_next;
230 uint32_t i_font_color; /* ARGB */
231 uint32_t i_karaoke_bg_color; /* ARGB */
238 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
239 static void FreeLines( line_desc_t * );
240 static void FreeLine( line_desc_t * );
241 #ifdef HAVE_FONTCONFIG
242 static void FontBuilder( vlc_object_t *p_this);
245 /*****************************************************************************
246 * filter_sys_t: freetype local data
247 *****************************************************************************
248 * This structure is part of the video output thread descriptor.
249 * It describes the freetype specific properties of an output thread.
250 *****************************************************************************/
253 FT_Library p_library; /* handle to library */
254 FT_Face p_face; /* handle to face object */
256 uint8_t i_font_opacity;
261 int i_default_font_size;
262 int i_display_height;
263 #ifdef HAVE_FONTCONFIG
264 FcConfig *p_fontconfig;
265 bool b_fontconfig_ok;
266 vlc_mutex_t fontconfig_lock;
269 input_attachment_t **pp_font_attachments;
270 int i_font_attachments;
273 /*****************************************************************************
274 * Create: allocates osd-text video thread output method
275 *****************************************************************************
276 * This function allocates and initializes a Clone vout method.
277 *****************************************************************************/
278 static int Create( vlc_object_t *p_this )
280 filter_t *p_filter = (filter_t *)p_this;
282 char *psz_fontfile = NULL;
285 vlc_object_t *p_fontbuilder;
287 /* Allocate structure */
288 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
291 msg_Err( p_filter, "out of memory" );
295 p_sys->p_library = 0;
296 p_sys->i_font_size = 0;
297 p_sys->i_display_height = 0;
299 var_Create( p_filter, "freetype-font",
300 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
301 var_Create( p_filter, "freetype-fontsize",
302 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
303 var_Create( p_filter, "freetype-rel-fontsize",
304 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
305 var_Create( p_filter, "freetype-opacity",
306 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
307 var_Create( p_filter, "freetype-effect",
308 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
309 var_Get( p_filter, "freetype-opacity", &val );
310 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
311 var_Create( p_filter, "freetype-color",
312 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
313 var_Get( p_filter, "freetype-color", &val );
314 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
315 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
317 /* Look what method was requested */
318 var_Get( p_filter, "freetype-font", &val );
319 psz_fontfile = val.psz_string;
320 if( !psz_fontfile || !*psz_fontfile )
322 free( psz_fontfile );
323 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
326 msg_Err( p_filter, "out of memory" );
330 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
331 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
332 #elif defined(__APPLE__)
333 strcpy( psz_fontfile, DEFAULT_FONT );
335 msg_Err( p_filter, "user didn't specify a font" );
340 i_error = FT_Init_FreeType( &p_sys->p_library );
343 msg_Err( p_filter, "couldn't initialize freetype" );
346 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
348 if( i_error == FT_Err_Unknown_File_Format )
350 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
355 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
359 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
362 msg_Err( p_filter, "font has no unicode translation table" );
366 #ifdef HAVE_FONTCONFIG
367 vlc_mutex_init( &p_sys->fontconfig_lock );
368 p_sys->b_fontconfig_ok = false;
369 p_sys->p_fontconfig = NULL;
371 /* Check for an existing Fontbuilder thread */
372 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
373 p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
377 if( ! p_fontbuilder )
379 /* Create the FontBuilder thread as a child of a top-level
380 * object, so that it can survive the destruction of the
381 * freetype object - the fontlist only needs to be built once,
382 * and calling the fontbuild a second time while the first is
383 * still in progress can cause thread instabilities.
386 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
387 VLC_OBJECT_GENERIC );
390 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
391 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
393 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
394 var_SetBool( p_fontbuilder, "build-done", false );
395 var_AddCallback( p_fontbuilder, "build-done", BuildDone, p_sys );
397 if( vlc_thread_create( p_fontbuilder,
400 VLC_THREAD_PRIORITY_LOW,
403 /* Don't destroy the fontconfig object - we won't be able to do
404 * italics or bold or change the font face, but we will still
405 * be able to do underline and change the font size.
407 msg_Warn( p_filter, "fontconfig database builder thread can't "
408 "be launched. Font styling support will be limited." );
413 vlc_object_release( p_fontbuilder );
418 vlc_object_release( p_fontbuilder );
420 vlc_mutex_unlock( lock );
424 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
426 var_Get( p_filter, "freetype-fontsize", &val );
427 p_sys->i_default_font_size = val.i_int;
428 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
430 free( psz_fontfile );
432 p_sys->pp_font_attachments = NULL;
433 p_sys->i_font_attachments = 0;
435 p_filter->pf_render_text = RenderText;
436 #ifdef HAVE_FONTCONFIG
437 p_filter->pf_render_html = RenderHtml;
439 p_filter->pf_render_html = NULL;
442 LoadFontsFromAttachments( p_filter );
447 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
448 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
449 free( psz_fontfile );
454 /*****************************************************************************
455 * Destroy: destroy Clone video thread output method
456 *****************************************************************************
457 * Clean up all data and library connections
458 *****************************************************************************/
459 static void Destroy( vlc_object_t *p_this )
461 filter_t *p_filter = (filter_t *)p_this;
462 filter_sys_t *p_sys = p_filter->p_sys;
464 if( p_sys->pp_font_attachments )
468 for( k = 0; k < p_sys->i_font_attachments; k++ )
470 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
473 free( p_sys->pp_font_attachments );
476 #ifdef HAVE_FONTCONFIG
477 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
478 vlc_object_t *p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
479 "fontlist builder", FIND_CHILD );
482 var_DelCallback( p_fontbuilder, "build-done", BuildDone, p_sys );
483 vlc_object_release( p_fontbuilder );
485 vlc_mutex_unlock( lock );
487 vlc_mutex_destroy( &p_sys->fontconfig_lock );
489 if( p_sys->p_fontconfig )
491 FcConfigDestroy( p_sys->p_fontconfig );
492 p_sys->p_fontconfig = NULL;
494 /* FcFini asserts calling the subfunction FcCacheFini()
495 * even if no other library functions have been made since FcInit(),
496 * so don't call it. */
498 FT_Done_Face( p_sys->p_face );
499 FT_Done_FreeType( p_sys->p_library );
503 #ifdef HAVE_FONTCONFIG
505 static void FontBuilder( vlc_object_t *p_this )
507 FcConfig *p_fontconfig = FcInitLoadConfig();
510 vlc_thread_ready( p_this );
516 msg_Dbg( p_this, "Building font database..." );
518 if(! FcConfigBuildFonts( p_fontconfig ))
520 /* Don't destroy the fontconfig object - we won't be able to do
521 * italics or bold or change the font face, but we will still
522 * be able to do underline and change the font size.
524 msg_Err( p_this, "fontconfig database can't be built. "
525 "Font styling won't be available" );
529 msg_Dbg( p_this, "Finished building font database." );
530 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
532 lock = var_AcquireMutex( "fontbuilder" );
534 var_SetBool( p_this, "build-done", true );
536 FcConfigDestroy( p_fontconfig );
537 vlc_mutex_unlock( lock );
539 vlc_object_detach( p_this );
540 vlc_object_release( p_this );
545 /*****************************************************************************
546 * Make any TTF/OTF fonts present in the attachments of the media file
547 * and store them for later use by the FreeType Engine
548 *****************************************************************************/
549 static int LoadFontsFromAttachments( filter_t *p_filter )
551 filter_sys_t *p_sys = p_filter->p_sys;
552 input_thread_t *p_input;
553 input_attachment_t **pp_attachments;
554 int i_attachments_cnt;
556 int rv = VLC_SUCCESS;
558 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
562 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
564 vlc_object_release(p_input);
568 p_sys->i_font_attachments = 0;
569 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
570 if(! p_sys->pp_font_attachments )
573 for( k = 0; k < i_attachments_cnt; k++ )
575 input_attachment_t *p_attach = pp_attachments[k];
577 if( p_sys->pp_font_attachments )
579 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
580 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
581 ( p_attach->i_data > 0 ) &&
582 ( p_attach->p_data != NULL ) )
584 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
588 vlc_input_attachment_Delete( p_attach );
593 vlc_input_attachment_Delete( p_attach );
596 free( pp_attachments );
598 vlc_object_release(p_input);
603 /*****************************************************************************
604 * Render: place string in picture
605 *****************************************************************************
606 * This function merges the previously rendered freetype glyphs into a picture
607 *****************************************************************************/
608 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
609 line_desc_t *p_line, int i_width, int i_height )
611 static uint8_t pi_gamma[16] =
612 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
613 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
617 int i, x, y, i_pitch;
618 uint8_t i_y; /* YUV values, derived from incoming RGB */
620 subpicture_region_t *p_region_tmp;
622 /* Create a new subpicture region */
623 memset( &fmt, 0, sizeof(video_format_t) );
624 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
626 fmt.i_width = fmt.i_visible_width = i_width + 4;
627 fmt.i_height = fmt.i_visible_height = i_height + 4;
628 if( p_region->fmt.i_visible_width > 0 )
629 fmt.i_visible_width = p_region->fmt.i_visible_width;
630 if( p_region->fmt.i_visible_height > 0 )
631 fmt.i_visible_height = p_region->fmt.i_visible_height;
632 fmt.i_x_offset = fmt.i_y_offset = 0;
633 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
636 msg_Err( p_filter, "cannot allocate SPU region" );
640 p_region->fmt = p_region_tmp->fmt;
641 p_region->picture = p_region_tmp->picture;
642 free( p_region_tmp );
644 /* Calculate text color components */
645 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
646 25 * p_line->i_blue + 128) >> 8) + 16;
647 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
648 112 * p_line->i_blue + 128) >> 8) + 128;
649 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
650 18 * p_line->i_blue + 128) >> 8) + 128;
653 fmt.p_palette->i_entries = 16;
654 for( i = 0; i < 8; i++ )
656 fmt.p_palette->palette[i][0] = 0;
657 fmt.p_palette->palette[i][1] = 0x80;
658 fmt.p_palette->palette[i][2] = 0x80;
659 fmt.p_palette->palette[i][3] = pi_gamma[i];
660 fmt.p_palette->palette[i][3] =
661 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
663 for( i = 8; i < fmt.p_palette->i_entries; i++ )
665 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
666 fmt.p_palette->palette[i][1] = i_u;
667 fmt.p_palette->palette[i][2] = i_v;
668 fmt.p_palette->palette[i][3] = pi_gamma[i];
669 fmt.p_palette->palette[i][3] =
670 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
673 p_dst = p_region->picture.Y_PIXELS;
674 i_pitch = p_region->picture.Y_PITCH;
676 /* Initialize the region pixels */
677 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
679 for( ; p_line != NULL; p_line = p_line->p_next )
681 int i_glyph_tmax = 0;
682 int i_bitmap_offset, i_offset, i_align_offset = 0;
683 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
685 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
686 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
689 if( p_line->i_width < i_width )
691 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
693 i_align_offset = i_width - p_line->i_width;
695 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
697 i_align_offset = ( i_width - p_line->i_width ) / 2;
701 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
703 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
705 i_offset = ( p_line->p_glyph_pos[ i ].y +
706 i_glyph_tmax - p_glyph->top + 2 ) *
707 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
710 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
712 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
714 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
716 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
723 /* Outlining (find something better than nearest neighbour filtering ?) */
726 uint8_t *p_dst = p_region->picture.Y_PIXELS;
727 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
728 uint8_t left, current;
730 for( y = 1; y < (int)fmt.i_height - 1; y++ )
732 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
733 p_dst += p_region->picture.Y_PITCH;
736 for( x = 1; x < (int)fmt.i_width - 1; x++ )
739 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
740 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;
744 memset( p_top, 0, fmt.i_width );
750 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
751 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
752 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
753 int i_glyph_tmax, int i_align_offset,
754 uint8_t i_y, uint8_t i_u, uint8_t i_v,
755 subpicture_region_t *p_region)
759 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
761 p_dst_y = p_region->picture.Y_PIXELS;
762 p_dst_u = p_region->picture.U_PIXELS;
763 p_dst_v = p_region->picture.V_PIXELS;
764 p_dst_a = p_region->picture.A_PIXELS;
765 i_pitch = p_region->picture.A_PITCH;
767 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
768 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
770 for( y = 0; y < i_line_thickness; y++ )
772 int i_extra = p_this_glyph->bitmap.width;
776 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
777 (p_this_glyph_pos->x + p_this_glyph->left);
779 for( x = 0; x < i_extra; x++ )
783 /* break the underline around the tails of any glyphs which cross it */
784 for( z = x - i_line_thickness;
785 z < x + i_line_thickness && b_ok;
788 if( p_next_glyph && ( z >= i_extra ) )
790 int i_row = i_line_offset + p_next_glyph->top + y;
792 if( ( p_next_glyph->bitmap.rows > i_row ) &&
793 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
798 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
800 int i_row = i_line_offset + p_this_glyph->top + y;
802 if( ( p_this_glyph->bitmap.rows > i_row ) &&
803 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
812 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
813 p_dst_u[i_offset+x] = i_u;
814 p_dst_v[i_offset+x] = i_v;
815 p_dst_a[i_offset+x] = 255;
822 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
824 uint8_t *p_dst = p_region->picture.A_PIXELS;
825 int i_pitch = p_region->picture.A_PITCH;
828 for( ; p_line != NULL; p_line = p_line->p_next )
830 int i_glyph_tmax=0, i = 0;
831 int i_bitmap_offset, i_offset, i_align_offset = 0;
832 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
834 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
835 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
838 if( p_line->i_width < i_width )
840 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
842 i_align_offset = i_width - p_line->i_width;
844 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
846 i_align_offset = ( i_width - p_line->i_width ) / 2;
850 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
852 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
854 i_offset = ( p_line->p_glyph_pos[ i ].y +
855 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
856 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
857 i_align_offset +xoffset;
859 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
861 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
863 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
864 if( p_dst[i_offset+x] <
865 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
867 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
876 /*****************************************************************************
877 * Render: place string in picture
878 *****************************************************************************
879 * This function merges the previously rendered freetype glyphs into a picture
880 *****************************************************************************/
881 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
882 line_desc_t *p_line, int i_width, int i_height )
884 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
886 int i, x, y, i_pitch, i_alpha;
887 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
888 subpicture_region_t *p_region_tmp;
890 if( i_width == 0 || i_height == 0 )
893 /* Create a new subpicture region */
894 memset( &fmt, 0, sizeof(video_format_t) );
895 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
897 fmt.i_width = fmt.i_visible_width = i_width + 6;
898 fmt.i_height = fmt.i_visible_height = i_height + 6;
899 if( p_region->fmt.i_visible_width > 0 )
900 fmt.i_visible_width = p_region->fmt.i_visible_width;
901 if( p_region->fmt.i_visible_height > 0 )
902 fmt.i_visible_height = p_region->fmt.i_visible_height;
903 fmt.i_x_offset = fmt.i_y_offset = 0;
904 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
907 msg_Err( p_filter, "cannot allocate SPU region" );
911 p_region->fmt = p_region_tmp->fmt;
912 p_region->picture = p_region_tmp->picture;
913 free( p_region_tmp );
915 /* Calculate text color components */
916 YUVFromRGB( (p_line->i_red << 16) |
917 (p_line->i_green << 8) |
920 i_alpha = p_line->i_alpha;
922 p_dst_y = p_region->picture.Y_PIXELS;
923 p_dst_u = p_region->picture.U_PIXELS;
924 p_dst_v = p_region->picture.V_PIXELS;
925 p_dst_a = p_region->picture.A_PIXELS;
926 i_pitch = p_region->picture.A_PITCH;
928 /* Initialize the region pixels */
929 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
931 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
932 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
933 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
934 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
938 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
939 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
940 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
941 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
943 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
944 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
946 DrawBlack( p_line, i_width, p_region, 0, 0);
947 DrawBlack( p_line, i_width, p_region, -1, 0);
948 DrawBlack( p_line, i_width, p_region, 0, -1);
949 DrawBlack( p_line, i_width, p_region, 1, 0);
950 DrawBlack( p_line, i_width, p_region, 0, 1);
953 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
955 DrawBlack( p_line, i_width, p_region, -1, -1);
956 DrawBlack( p_line, i_width, p_region, -1, 1);
957 DrawBlack( p_line, i_width, p_region, 1, -1);
958 DrawBlack( p_line, i_width, p_region, 1, 1);
960 DrawBlack( p_line, i_width, p_region, -2, 0);
961 DrawBlack( p_line, i_width, p_region, 0, -2);
962 DrawBlack( p_line, i_width, p_region, 2, 0);
963 DrawBlack( p_line, i_width, p_region, 0, 2);
965 DrawBlack( p_line, i_width, p_region, -2, -2);
966 DrawBlack( p_line, i_width, p_region, -2, 2);
967 DrawBlack( p_line, i_width, p_region, 2, -2);
968 DrawBlack( p_line, i_width, p_region, 2, 2);
970 DrawBlack( p_line, i_width, p_region, -3, 0);
971 DrawBlack( p_line, i_width, p_region, 0, -3);
972 DrawBlack( p_line, i_width, p_region, 3, 0);
973 DrawBlack( p_line, i_width, p_region, 0, 3);
976 for( ; p_line != NULL; p_line = p_line->p_next )
978 int i_glyph_tmax = 0;
979 int i_bitmap_offset, i_offset, i_align_offset = 0;
980 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
982 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
983 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
986 if( p_line->i_width < i_width )
988 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
990 i_align_offset = i_width - p_line->i_width;
992 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
994 i_align_offset = ( i_width - p_line->i_width ) / 2;
998 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1000 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1002 i_offset = ( p_line->p_glyph_pos[ i ].y +
1003 i_glyph_tmax - p_glyph->top + 3 ) *
1004 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1007 if( p_line->b_new_color_mode )
1009 /* Every glyph can (and in fact must) have its own color */
1010 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1013 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1015 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1017 uint8_t i_y_local = i_y;
1018 uint8_t i_u_local = i_u;
1019 uint8_t i_v_local = i_v;
1021 if( p_line->p_fg_bg_ratio != 0x00 )
1023 int i_split = p_glyph->bitmap.width *
1024 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1028 YUVFromRGB( p_line->p_bg_rgb[ i ],
1029 &i_y_local, &i_u_local, &i_v_local );
1033 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1035 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1036 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1038 p_dst_u[i_offset+x] = i_u;
1039 p_dst_v[i_offset+x] = i_v;
1041 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1042 p_dst_a[i_offset+x] = 0xff;
1045 i_offset += i_pitch;
1048 if( p_line->pi_underline_thickness[ i ] )
1050 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1051 p_line->pi_underline_offset[ i ],
1052 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1053 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1054 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1055 i_glyph_tmax, i_align_offset,
1062 /* Apply the alpha setting */
1063 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1064 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1070 * This function renders a text subpicture region into another one.
1071 * It also calculates the size needed for this string, and renders the
1072 * needed glyphs into memory. It is used as pf_add_string callback in
1073 * the vout method by this module
1075 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1076 subpicture_region_t *p_region_in )
1078 filter_sys_t *p_sys = p_filter->p_sys;
1079 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1080 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1081 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1082 int i_string_length;
1084 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1085 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1095 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1096 psz_string = p_region_in->psz_text;
1097 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1099 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1100 i_scale = val.i_int;
1102 if( p_region_in->p_style )
1104 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1105 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1106 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1110 i_font_color = p_sys->i_font_color;
1111 i_font_alpha = 255 - p_sys->i_font_opacity;
1112 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1115 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1116 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1117 SetFontSize( p_filter, i_font_size );
1119 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1120 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1121 i_blue = i_font_color & 0x000000FF;
1123 result.x = result.y = 0;
1124 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1126 psz_unicode = psz_unicode_orig =
1127 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1128 if( psz_unicode == NULL )
1130 msg_Err( p_filter, "out of memory" );
1133 #if defined(WORDS_BIGENDIAN)
1134 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1136 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1138 if( iconv_handle == (vlc_iconv_t)-1 )
1140 msg_Warn( p_filter, "unable to do conversion" );
1146 const char *p_in_buffer = psz_string;
1147 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1148 i_in_bytes = strlen( psz_string );
1149 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1150 i_out_bytes_left = i_out_bytes;
1151 p_out_buffer = (char *)psz_unicode;
1152 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1154 &p_out_buffer, &i_out_bytes_left );
1156 vlc_iconv_close( iconv_handle );
1160 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1161 "bytes left %u", (unsigned)i_in_bytes );
1164 *(uint32_t*)p_out_buffer = 0;
1165 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1168 #if defined(HAVE_FRIBIDI)
1170 uint32_t *p_fribidi_string;
1171 int32_t start_pos, pos = 0;
1173 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1174 if( !p_fribidi_string )
1176 msg_Err( p_filter, "out of memory" );
1180 /* Do bidi conversion line-by-line */
1181 while( pos < i_string_length )
1183 while( pos < i_string_length )
1185 i_char = psz_unicode[pos];
1186 if (i_char != '\r' && i_char != '\n')
1188 p_fribidi_string[pos] = i_char;
1192 while( pos < i_string_length )
1194 i_char = psz_unicode[pos];
1195 if (i_char == '\r' || i_char == '\n')
1199 if (pos > start_pos)
1201 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1202 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1205 (FriBidiChar*)p_fribidi_string + start_pos,
1210 free( psz_unicode_orig );
1211 psz_unicode = psz_unicode_orig = p_fribidi_string;
1212 p_fribidi_string[ i_string_length ] = 0;
1216 /* Calculate relative glyph positions and a bounding box for the
1218 if( !(p_line = NewLine( strlen( psz_string ))) )
1220 msg_Err( p_filter, "out of memory" );
1224 i_pen_x = i_pen_y = 0;
1226 psz_line_start = psz_unicode;
1228 #define face p_sys->p_face
1229 #define glyph face->glyph
1231 while( *psz_unicode )
1233 i_char = *psz_unicode++;
1234 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1239 if( i_char == '\n' )
1241 psz_line_start = psz_unicode;
1242 if( !(p_next = NewLine( strlen( psz_string ))) )
1244 msg_Err( p_filter, "out of memory" );
1247 p_line->p_next = p_next;
1248 p_line->i_width = line.xMax;
1249 p_line->i_height = face->size->metrics.height >> 6;
1250 p_line->pp_glyphs[ i ] = NULL;
1251 p_line->i_alpha = i_font_alpha;
1252 p_line->i_red = i_red;
1253 p_line->i_green = i_green;
1254 p_line->i_blue = i_blue;
1257 result.x = __MAX( result.x, line.xMax );
1258 result.y += face->size->metrics.height >> 6;
1261 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1262 i_pen_y += face->size->metrics.height >> 6;
1264 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1269 i_glyph_index = FT_Get_Char_Index( face, i_char );
1270 if( p_sys->i_use_kerning && i_glyph_index
1274 FT_Get_Kerning( face, i_previous, i_glyph_index,
1275 ft_kerning_default, &delta );
1276 i_pen_x += delta.x >> 6;
1279 p_line->p_glyph_pos[ i ].x = i_pen_x;
1280 p_line->p_glyph_pos[ i ].y = i_pen_y;
1281 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1284 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1288 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1291 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1295 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1296 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1299 FT_Done_Glyph( tmp_glyph );
1302 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1305 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1306 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1307 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1309 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1310 p_line->pp_glyphs[ i ] = NULL;
1312 p_line = NewLine( strlen( psz_string ));
1313 if( p_prev ) p_prev->p_next = p_line;
1314 else p_lines = p_line;
1316 uint32_t *psz_unicode_saved = psz_unicode;
1317 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1321 if( psz_unicode == psz_line_start )
1322 { /* try harder to break that line */
1323 psz_unicode = psz_unicode_saved;
1324 while( psz_unicode > psz_line_start &&
1325 *psz_unicode != '_' && *psz_unicode != '/' &&
1326 *psz_unicode != '\\' && *psz_unicode != '.' )
1331 if( psz_unicode == psz_line_start )
1333 msg_Warn( p_filter, "unbreakable string" );
1338 *psz_unicode = '\n';
1340 psz_unicode = psz_line_start;
1343 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1346 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1347 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1349 i_previous = i_glyph_index;
1350 i_pen_x += glyph->advance.x >> 6;
1354 p_line->i_width = line.xMax;
1355 p_line->i_height = face->size->metrics.height >> 6;
1356 p_line->pp_glyphs[ i ] = NULL;
1357 p_line->i_alpha = i_font_alpha;
1358 p_line->i_red = i_red;
1359 p_line->i_green = i_green;
1360 p_line->i_blue = i_blue;
1361 result.x = __MAX( result.x, line.xMax );
1362 result.y += line.yMax - line.yMin;
1367 p_region_out->i_x = p_region_in->i_x;
1368 p_region_out->i_y = p_region_in->i_y;
1370 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1371 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1373 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1375 free( psz_unicode_orig );
1376 FreeLines( p_lines );
1380 free( psz_unicode_orig );
1381 FreeLines( p_lines );
1382 return VLC_EGENERIC;
1385 #ifdef HAVE_FONTCONFIG
1386 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1387 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1388 bool b_italic, bool b_uline )
1390 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1394 p_style->i_font_size = i_font_size;
1395 p_style->i_font_color = i_font_color;
1396 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1397 p_style->b_italic = b_italic;
1398 p_style->b_bold = b_bold;
1399 p_style->b_underline = b_uline;
1401 p_style->psz_fontname = strdup( psz_fontname );
1406 static void DeleteStyle( ft_style_t *p_style )
1410 free( p_style->psz_fontname );
1415 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1422 if(( s1->i_font_size == s2->i_font_size ) &&
1423 ( s1->i_font_color == s2->i_font_color ) &&
1424 ( s1->b_italic == s2->b_italic ) &&
1425 ( s1->b_bold == s2->b_bold ) &&
1426 ( s1->b_underline == s2->b_underline ) &&
1427 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1434 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1435 uint32_t i_color, uint32_t i_karaoke_bg_color )
1437 font_stack_t *p_new;
1440 return VLC_EGENERIC;
1442 p_new = malloc( sizeof( font_stack_t ) );
1446 p_new->p_next = NULL;
1449 p_new->psz_name = strdup( psz_name );
1451 p_new->psz_name = NULL;
1453 p_new->i_size = i_size;
1454 p_new->i_color = i_color;
1455 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1463 font_stack_t *p_last;
1465 for( p_last = *p_font;
1467 p_last = p_last->p_next )
1470 p_last->p_next = p_new;
1475 static int PopFont( font_stack_t **p_font )
1477 font_stack_t *p_last, *p_next_to_last;
1479 if( !p_font || !*p_font )
1480 return VLC_EGENERIC;
1482 p_next_to_last = NULL;
1483 for( p_last = *p_font;
1485 p_last = p_last->p_next )
1487 p_next_to_last = p_last;
1490 if( p_next_to_last )
1491 p_next_to_last->p_next = NULL;
1495 free( p_last->psz_name );
1501 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1502 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1504 font_stack_t *p_last;
1506 if( !p_font || !*p_font )
1507 return VLC_EGENERIC;
1509 for( p_last=*p_font;
1511 p_last=p_last->p_next )
1514 *psz_name = p_last->psz_name;
1515 *i_size = p_last->i_size;
1516 *i_color = p_last->i_color;
1517 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1522 static void IconvText( filter_t *p_filter, const char *psz_string,
1523 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1525 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1527 /* If memory hasn't been allocated for our output string, allocate it here
1528 * - the calling function must now be responsible for freeing it.
1530 if( !*ppsz_unicode )
1531 *ppsz_unicode = (uint32_t *)
1532 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1534 /* We don't need to handle a NULL pointer in *ppsz_unicode
1535 * if we are instead testing for a non NULL value like we are here */
1539 #if defined(WORDS_BIGENDIAN)
1540 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1542 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1544 if( iconv_handle != (vlc_iconv_t)-1 )
1546 char *p_in_buffer, *p_out_buffer;
1547 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1548 i_in_bytes = strlen( psz_string );
1549 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1550 i_out_bytes_left = i_out_bytes;
1551 p_in_buffer = (char *) psz_string;
1552 p_out_buffer = (char *) *ppsz_unicode;
1553 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1554 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1556 vlc_iconv_close( iconv_handle );
1560 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1561 "bytes left %u", (unsigned)i_in_bytes );
1565 *(uint32_t*)p_out_buffer = 0;
1567 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1572 msg_Warn( p_filter, "unable to do conversion" );
1577 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1578 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1581 ft_style_t *p_style = NULL;
1583 char *psz_fontname = NULL;
1584 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1585 uint32_t i_karaoke_bg_color = i_font_color;
1586 int i_font_size = p_sys->i_font_size;
1588 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1589 &i_font_color, &i_karaoke_bg_color ))
1591 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1592 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1597 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1598 bool b_uline, int i_karaoke_bgcolor,
1599 line_desc_t *p_line, uint32_t *psz_unicode,
1600 int *pi_pen_x, int i_pen_y, int *pi_start,
1601 FT_Vector *p_result )
1606 bool b_first_on_line = true;
1609 int i_pen_x_start = *pi_pen_x;
1611 uint32_t *psz_unicode_start = psz_unicode;
1613 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1615 /* Account for part of line already in position */
1616 for( i=0; i<*pi_start; i++ )
1620 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1621 ft_glyph_bbox_pixels, &glyph_size );
1623 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1624 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1625 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1626 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1632 b_first_on_line = false;
1634 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1640 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1641 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1645 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1646 ft_kerning_default, &delta );
1647 *pi_pen_x += delta.x >> 6;
1649 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1650 p_line->p_glyph_pos[ i ].y = i_pen_y;
1652 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1656 "unable to render text FT_Load_Glyph returned %d", i_error );
1657 p_line->pp_glyphs[ i ] = NULL;
1658 return VLC_EGENERIC;
1660 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1664 "unable to render text FT_Get_Glyph returned %d", i_error );
1665 p_line->pp_glyphs[ i ] = NULL;
1666 return VLC_EGENERIC;
1668 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1669 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1672 FT_Done_Glyph( tmp_glyph );
1677 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1678 p_face->size->metrics.y_scale));
1679 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1680 p_face->size->metrics.y_scale));
1682 p_line->pi_underline_offset[ i ] =
1683 ( aOffset < 0 ) ? -aOffset : aOffset;
1684 p_line->pi_underline_thickness[ i ] =
1685 ( aSize < 0 ) ? -aSize : aSize;
1687 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1688 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1689 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1690 p_line->p_fg_bg_ratio[ i ] = 0x00;
1692 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1693 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1694 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1696 while( --i > *pi_start )
1698 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1701 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1705 if( psz_unicode == psz_unicode_start )
1707 if( b_first_on_line )
1709 msg_Warn( p_filter, "unbreakable string" );
1710 p_line->pp_glyphs[ i ] = NULL;
1711 return VLC_EGENERIC;
1713 *pi_pen_x = i_pen_x_start;
1715 p_line->i_width = line.xMax;
1716 p_line->i_height = __MAX( p_line->i_height,
1717 p_face->size->metrics.height >> 6 );
1718 p_line->pp_glyphs[ i ] = NULL;
1720 p_result->x = __MAX( p_result->x, line.xMax );
1721 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1722 i_yMax - i_yMin ) );
1729 *psz_unicode = '\n';
1731 psz_unicode = psz_unicode_start;
1732 *pi_pen_x = i_pen_x_start;
1740 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1741 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1743 i_previous = i_glyph_index;
1744 *pi_pen_x += p_face->glyph->advance.x >> 6;
1747 p_line->i_width = line.xMax;
1748 p_line->i_height = __MAX( p_line->i_height,
1749 p_face->size->metrics.height >> 6 );
1750 p_line->pp_glyphs[ i ] = NULL;
1752 p_result->x = __MAX( p_result->x, line.xMax );
1753 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1754 line.yMax - line.yMin ) );
1758 /* Get rid of any text processed - if necessary repositioning
1759 * at the start of a new line of text
1763 *psz_unicode_start = '\0';
1765 else if( psz_unicode > psz_unicode_start )
1767 for( i=0; psz_unicode[ i ]; i++ )
1768 psz_unicode_start[ i ] = psz_unicode[ i ];
1769 psz_unicode_start[ i ] = '\0';
1775 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1776 font_stack_t **p_fonts, int i_scale )
1779 char *psz_fontname = NULL;
1780 uint32_t i_font_color = 0xffffff;
1781 int i_font_alpha = 0;
1782 uint32_t i_karaoke_bg_color = 0x00ffffff;
1783 int i_font_size = 24;
1785 /* Default all attributes to the top font in the stack -- in case not
1786 * all attributes are specified in the sub-font
1788 if( VLC_SUCCESS == PeekFont( p_fonts,
1792 &i_karaoke_bg_color ))
1794 psz_fontname = strdup( psz_fontname );
1795 i_font_size = i_font_size * 1000 / i_scale;
1797 i_font_alpha = (i_font_color >> 24) & 0xff;
1798 i_font_color &= 0x00ffffff;
1800 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1802 char *psz_name = xml_ReaderName( p_xml_reader );
1803 char *psz_value = xml_ReaderValue( p_xml_reader );
1805 if( psz_name && psz_value )
1807 if( !strcasecmp( "face", psz_name ) )
1809 free( psz_fontname );
1810 psz_fontname = strdup( psz_value );
1812 else if( !strcasecmp( "size", psz_name ) )
1814 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1816 int i_value = atoi( psz_value );
1818 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1819 i_font_size += ( i_value * i_font_size ) / 10;
1820 else if( i_value < -5 )
1821 i_font_size = - i_value;
1822 else if( i_value > 5 )
1823 i_font_size = i_value;
1826 i_font_size = atoi( psz_value );
1828 else if( !strcasecmp( "color", psz_name ) &&
1829 ( psz_value[0] == '#' ) )
1831 i_font_color = strtol( psz_value + 1, NULL, 16 );
1832 i_font_color &= 0x00ffffff;
1834 else if( !strcasecmp( "alpha", psz_name ) &&
1835 ( psz_value[0] == '#' ) )
1837 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1838 i_font_alpha &= 0xff;
1844 rv = PushFont( p_fonts,
1846 i_font_size * i_scale / 1000,
1847 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1848 i_karaoke_bg_color );
1850 free( psz_fontname );
1855 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1856 uint32_t **psz_text_out, uint32_t *pi_runs,
1857 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1858 ft_style_t *p_style )
1860 uint32_t i_string_length = 0;
1862 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1863 *psz_text_out += i_string_length;
1865 if( ppp_styles && ppi_run_lengths )
1871 *ppp_styles = (ft_style_t **)
1872 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1874 else if( *pi_runs == 1 )
1876 *ppp_styles = (ft_style_t **)
1877 malloc( *pi_runs * sizeof( ft_style_t * ) );
1880 /* We have just malloc'ed this memory successfully -
1881 * *pi_runs HAS to be within the memory area of *ppp_styles */
1884 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1888 if( *ppi_run_lengths )
1890 *ppi_run_lengths = (uint32_t *)
1891 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1893 else if( *pi_runs == 1 )
1895 *ppi_run_lengths = (uint32_t *)
1896 malloc( *pi_runs * sizeof( uint32_t ) );
1899 /* same remarks here */
1900 if( *ppi_run_lengths )
1902 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1905 /* If we couldn't use the p_style argument due to memory allocation
1906 * problems above, release it here.
1908 if( p_style ) DeleteStyle( p_style );
1911 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1912 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1914 /* Karaoke tags _PRECEDE_ the text they specify a duration
1915 * for, therefore we are working out the length for the
1916 * previous tag, and first time through we have nothing
1918 if( pi_k_run_lengths )
1923 /* Work out how many characters are presently in the string
1925 for( i = 0; i < i_runs; i++ )
1926 i_chars += pi_run_lengths[ i ];
1928 /* Subtract away those we've already allocated to other
1931 for( i = 0; i < i_k_runs; i++ )
1932 i_chars -= pi_k_run_lengths[ i ];
1934 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1938 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1939 uint32_t **ppi_k_run_lengths,
1940 uint32_t **ppi_k_durations )
1942 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1944 char *psz_name = xml_ReaderName( p_xml_reader );
1945 char *psz_value = xml_ReaderValue( p_xml_reader );
1947 if( psz_name && psz_value &&
1948 !strcasecmp( "t", psz_name ) )
1950 if( ppi_k_durations && ppi_k_run_lengths )
1954 if( *ppi_k_durations )
1956 *ppi_k_durations = (uint32_t *)
1957 realloc( *ppi_k_durations,
1958 *pi_k_runs * sizeof( uint32_t ) );
1960 else if( *pi_k_runs == 1 )
1962 *ppi_k_durations = (uint32_t *)
1963 malloc( *pi_k_runs * sizeof( uint32_t ) );
1966 if( *ppi_k_run_lengths )
1968 *ppi_k_run_lengths = (uint32_t *)
1969 realloc( *ppi_k_run_lengths,
1970 *pi_k_runs * sizeof( uint32_t ) );
1972 else if( *pi_k_runs == 1 )
1974 *ppi_k_run_lengths = (uint32_t *)
1975 malloc( *pi_k_runs * sizeof( uint32_t ) );
1977 if( *ppi_k_durations )
1978 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1980 if( *ppi_k_run_lengths )
1981 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1989 static int ProcessNodes( filter_t *p_filter,
1990 xml_reader_t *p_xml_reader,
1991 text_style_t *p_font_style,
1996 uint32_t **ppi_run_lengths,
1997 ft_style_t ***ppp_styles,
2000 uint32_t *pi_k_runs,
2001 uint32_t **ppi_k_run_lengths,
2002 uint32_t **ppi_k_durations )
2004 int rv = VLC_SUCCESS;
2005 filter_sys_t *p_sys = p_filter->p_sys;
2006 uint32_t *psz_text_orig = psz_text;
2007 font_stack_t *p_fonts = NULL;
2011 char *psz_node = NULL;
2013 bool b_italic = false;
2014 bool b_bold = false;
2015 bool b_uline = false;
2017 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2018 i_scale = val.i_int;
2022 rv = PushFont( &p_fonts,
2023 p_font_style->psz_fontname,
2024 p_font_style->i_font_size * i_scale / 1000,
2025 (p_font_style->i_font_color & 0xffffff) |
2026 ((p_font_style->i_font_alpha & 0xff) << 24),
2027 (p_font_style->i_karaoke_background_color & 0xffffff) |
2028 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2030 if( p_font_style->i_style_flags & STYLE_BOLD )
2032 if( p_font_style->i_style_flags & STYLE_ITALIC )
2034 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2039 rv = PushFont( &p_fonts,
2045 if( rv != VLC_SUCCESS )
2048 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2050 switch ( xml_ReaderNodeType( p_xml_reader ) )
2052 case XML_READER_NONE:
2054 case XML_READER_ENDELEM:
2055 psz_node = xml_ReaderName( p_xml_reader );
2059 if( !strcasecmp( "font", psz_node ) )
2060 PopFont( &p_fonts );
2061 else if( !strcasecmp( "b", psz_node ) )
2063 else if( !strcasecmp( "i", psz_node ) )
2065 else if( !strcasecmp( "u", psz_node ) )
2071 case XML_READER_STARTELEM:
2072 psz_node = xml_ReaderName( p_xml_reader );
2075 if( !strcasecmp( "font", psz_node ) )
2076 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2077 else if( !strcasecmp( "b", psz_node ) )
2079 else if( !strcasecmp( "i", psz_node ) )
2081 else if( !strcasecmp( "u", psz_node ) )
2083 else if( !strcasecmp( "br", psz_node ) )
2085 SetupLine( p_filter, "\n", &psz_text,
2086 pi_runs, ppi_run_lengths, ppp_styles,
2087 GetStyleFromFontStack( p_sys,
2093 else if( !strcasecmp( "k", psz_node ) )
2095 /* Only valid in karaoke */
2098 if( *pi_k_runs > 0 )
2100 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2101 *pi_k_runs, *ppi_k_run_lengths );
2103 SetupKaraoke( p_xml_reader, pi_k_runs,
2104 ppi_k_run_lengths, ppi_k_durations );
2111 case XML_READER_TEXT:
2112 psz_node = xml_ReaderValue( p_xml_reader );
2115 /* Turn any multiple-whitespaces into single spaces */
2116 char *s = strpbrk( psz_node, "\t\r\n " );
2119 int i_whitespace = strspn( s, "\t\r\n " );
2121 if( i_whitespace > 1 )
2124 strlen( s ) - i_whitespace + 1 );
2127 s = strpbrk( s, "\t\r\n " );
2129 SetupLine( p_filter, psz_node, &psz_text,
2130 pi_runs, ppi_run_lengths, ppp_styles,
2131 GetStyleFromFontStack( p_sys,
2140 if( rv != VLC_SUCCESS )
2142 psz_text = psz_text_orig;
2148 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2149 *pi_k_runs, *ppi_k_run_lengths );
2152 *pi_len = psz_text - psz_text_orig;
2154 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2159 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2163 for( k=0; k < p_sys->i_font_attachments; k++ )
2165 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2167 FT_Face p_face = NULL;
2169 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2177 bool match = !strcasecmp( p_face->family_name,
2178 p_style->psz_fontname );
2180 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2181 match = match && p_style->b_bold;
2183 match = match && !p_style->b_bold;
2185 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2186 match = match && p_style->b_italic;
2188 match = match && !p_style->b_italic;
2196 FT_Done_Face( p_face );
2201 return VLC_EGENERIC;
2204 static int BuildDone( vlc_object_t *p_this, const char *psz_var,
2205 vlc_value_t oldval, vlc_value_t newval, void *param )
2210 ((filter_sys_t*)param)->b_fontconfig_ok = newval.b_bool;
2211 assert( newval.b_bool );
2215 static int ProcessLines( filter_t *p_filter,
2220 uint32_t *pi_run_lengths,
2221 ft_style_t **pp_styles,
2222 line_desc_t **pp_lines,
2224 FT_Vector *p_result,
2228 uint32_t *pi_k_run_lengths,
2229 uint32_t *pi_k_durations )
2231 filter_sys_t *p_sys = p_filter->p_sys;
2232 ft_style_t **pp_char_styles;
2233 int *p_new_positions = NULL;
2234 int8_t *p_levels = NULL;
2235 uint8_t *pi_karaoke_bar = NULL;
2239 /* Assign each character in the text string its style explicitly, so that
2240 * after the characters have been shuffled around by Fribidi, we can re-apply
2241 * the styles, and to simplify the calculation of runs within a line.
2243 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2244 if( !pp_char_styles )
2249 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2250 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2251 * we just won't be able to display the progress bar; at least we'll
2257 for( j = 0; j < i_runs; j++ )
2258 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2259 pp_char_styles[ i++ ] = pp_styles[ j ];
2261 #if defined(HAVE_FRIBIDI)
2263 ft_style_t **pp_char_styles_new;
2264 int *p_old_positions;
2265 uint32_t *p_fribidi_string;
2266 int start_pos, pos = 0;
2268 pp_char_styles_new = (ft_style_t **)
2269 malloc( i_len * sizeof( ft_style_t * ));
2271 p_fribidi_string = (uint32_t *)
2272 malloc( (i_len + 1) * sizeof(uint32_t) );
2273 p_old_positions = (int *)
2274 malloc( (i_len + 1) * sizeof( int ) );
2275 p_new_positions = (int *)
2276 malloc( (i_len + 1) * sizeof( int ) );
2277 p_levels = (int8_t *)
2278 malloc( (i_len + 1) * sizeof( int8_t ) );
2280 if( ! pp_char_styles_new ||
2281 ! p_fribidi_string ||
2282 ! p_old_positions ||
2283 ! p_new_positions ||
2286 msg_Err( p_filter, "out of memory" );
2288 free( p_old_positions );
2289 free( p_new_positions );
2290 free( p_fribidi_string );
2291 free( pp_char_styles_new );
2292 free( pi_karaoke_bar );
2294 free( pp_char_styles );
2298 /* Do bidi conversion line-by-line */
2301 while(pos < i_len) {
2302 if (psz_text[pos] != '\n')
2304 p_fribidi_string[pos] = psz_text[pos];
2305 pp_char_styles_new[pos] = pp_char_styles[pos];
2306 p_new_positions[pos] = pos;
2311 while(pos < i_len) {
2312 if (psz_text[pos] == '\n')
2316 if (pos > start_pos)
2318 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2319 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2320 pos - start_pos, &base_dir,
2321 (FriBidiChar*)p_fribidi_string + start_pos,
2322 p_new_positions + start_pos,
2324 p_levels + start_pos );
2325 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2327 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2328 p_old_positions[ j - start_pos ] ];
2329 p_new_positions[ j ] += start_pos;
2333 free( p_old_positions );
2334 free( pp_char_styles );
2335 pp_char_styles = pp_char_styles_new;
2336 psz_text = p_fribidi_string;
2337 p_fribidi_string[ i_len ] = 0;
2340 /* Work out the karaoke */
2341 if( pi_karaoke_bar )
2343 int64_t i_last_duration = 0;
2344 int64_t i_duration = 0;
2345 int64_t i_start_pos = 0;
2346 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2348 for( k = 0; k< i_k_runs; k++ )
2350 double fraction = 0.0;
2352 i_duration += pi_k_durations[ k ];
2354 if( i_duration < i_elapsed )
2356 /* Completely finished this run-length -
2357 * let it render normally */
2361 else if( i_elapsed < i_last_duration )
2363 /* Haven't got up to this segment yet -
2364 * render it completely in karaoke BG mode */
2370 /* Partway through this run */
2372 fraction = (double)(i_elapsed - i_last_duration) /
2373 (double)pi_k_durations[ k ];
2375 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2377 double shade = pi_k_run_lengths[ k ] * fraction;
2379 if( p_new_positions )
2380 j = p_new_positions[ i_start_pos + i ];
2382 j = i_start_pos + i;
2384 if( i < (uint32_t)shade )
2385 pi_karaoke_bar[ j ] = 0xff;
2386 else if( (double)i > shade )
2387 pi_karaoke_bar[ j ] = 0x00;
2390 shade -= (int)shade;
2391 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2392 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2396 i_last_duration = i_duration;
2397 i_start_pos += pi_k_run_lengths[ k ];
2401 free( p_new_positions );
2403 FT_Vector tmp_result;
2405 line_desc_t *p_line = NULL;
2406 line_desc_t *p_prev = NULL;
2412 p_result->x = p_result->y = 0;
2413 tmp_result.x = tmp_result.y = 0;
2416 for( k = 0; k <= (uint32_t) i_len; k++ )
2418 if( ( k == (uint32_t) i_len ) ||
2420 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2422 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2424 /* End of the current style run */
2425 FT_Face p_face = NULL;
2428 /* Look for a match amongst our attachments first */
2429 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2431 if( ! p_face && p_sys->b_fontconfig_ok )
2434 vlc_mutex_lock( &p_sys->fontconfig_lock );
2436 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2437 p_style->psz_fontname,
2441 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2443 if( psz_fontfile && ! *psz_fontfile )
2445 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2446 " so using default font", p_style->psz_fontname,
2447 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2448 (p_style->b_bold ? "(Bold)" :
2449 (p_style->b_italic ? "(Italic)" : ""))) );
2450 free( psz_fontfile );
2451 psz_fontfile = NULL;
2456 if( FT_New_Face( p_sys->p_library,
2457 psz_fontfile, i_idx, &p_face ) )
2459 free( psz_fontfile );
2460 free( pp_char_styles );
2461 #if defined(HAVE_FRIBIDI)
2464 free( pi_karaoke_bar );
2465 return VLC_EGENERIC;
2467 free( psz_fontfile );
2471 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2473 /* We've loaded a font face which is unhelpful for actually
2474 * rendering text - fallback to the default one.
2476 FT_Done_Face( p_face );
2480 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2481 ft_encoding_unicode ) ||
2482 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2483 p_style->i_font_size ) )
2485 if( p_face ) FT_Done_Face( p_face );
2486 free( pp_char_styles );
2487 #if defined(HAVE_FRIBIDI)
2490 free( pi_karaoke_bar );
2491 return VLC_EGENERIC;
2493 p_sys->i_use_kerning =
2494 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2497 uint32_t *psz_unicode = (uint32_t *)
2498 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2501 msg_Err( p_filter, "out of memory" );
2502 if( p_face ) FT_Done_Face( p_face );
2503 free( pp_char_styles );
2504 free( psz_unicode );
2505 #if defined(HAVE_FRIBIDI)
2508 free( pi_karaoke_bar );
2511 memcpy( psz_unicode, psz_text + i_prev,
2512 sizeof( uint32_t ) * ( k - i_prev ) );
2513 psz_unicode[ k - i_prev ] = 0;
2514 while( *psz_unicode )
2518 if( !(p_line = NewLine( i_len - i_prev)) )
2520 msg_Err( p_filter, "out of memory" );
2521 if( p_face ) FT_Done_Face( p_face );
2522 free( pp_char_styles );
2523 free( psz_unicode );
2524 #if defined(HAVE_FRIBIDI)
2527 free( pi_karaoke_bar );
2530 /* New Color mode only works in YUVA rendering mode --
2531 * (RGB mode has palette constraints on it). We therefore
2532 * need to populate the legacy colour fields also.
2534 p_line->b_new_color_mode = true;
2535 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2536 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2537 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2538 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2539 p_line->p_next = NULL;
2541 i_pen_y += tmp_result.y;
2545 if( p_prev ) p_prev->p_next = p_line;
2546 else *pp_lines = p_line;
2549 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2550 p_style->i_font_color, p_style->b_underline,
2551 p_style->i_karaoke_bg_color,
2552 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2553 &tmp_result ) != VLC_SUCCESS )
2555 if( p_face ) FT_Done_Face( p_face );
2556 free( pp_char_styles );
2557 free( psz_unicode );
2558 #if defined(HAVE_FRIBIDI)
2561 free( pi_karaoke_bar );
2562 return VLC_EGENERIC;
2567 p_result->x = __MAX( p_result->x, tmp_result.x );
2568 p_result->y += tmp_result.y;
2573 if( *psz_unicode == '\n')
2577 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2579 *c_ptr = *(c_ptr+1);
2584 free( psz_unicode );
2585 if( p_face ) FT_Done_Face( p_face );
2589 free( pp_char_styles );
2590 #if defined(HAVE_FRIBIDI)
2595 p_result->x = __MAX( p_result->x, tmp_result.x );
2596 p_result->y += tmp_result.y;
2599 if( pi_karaoke_bar )
2602 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2604 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2606 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2610 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2612 /* 100% BG colour will render faster if we
2613 * instead make it 100% FG colour, so leave
2614 * the ratio alone and copy the value across
2616 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2620 if( pi_karaoke_bar[ i ] & 0x80 )
2622 /* Swap Left and Right sides over for Right aligned
2623 * language text (eg. Arabic, Hebrew)
2625 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2627 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2628 p_line->p_bg_rgb[ k ] = i_tmp;
2630 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2633 /* Jump over the '\n' at the line-end */
2636 free( pi_karaoke_bar );
2642 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2643 subpicture_region_t *p_region_in )
2645 int rv = VLC_SUCCESS;
2646 stream_t *p_sub = NULL;
2647 xml_t *p_xml = NULL;
2648 xml_reader_t *p_xml_reader = NULL;
2650 if( !p_region_in || !p_region_in->psz_html )
2651 return VLC_EGENERIC;
2653 /* Reset the default fontsize in case screen metrics have changed */
2654 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2656 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2657 (uint8_t *) p_region_in->psz_html,
2658 strlen( p_region_in->psz_html ),
2662 p_xml = xml_Create( p_filter );
2665 bool b_karaoke = false;
2667 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2670 /* Look for Root Node */
2671 if( xml_ReaderRead( p_xml_reader ) == 1 )
2673 char *psz_node = xml_ReaderName( p_xml_reader );
2675 if( !strcasecmp( "karaoke", psz_node ) )
2677 /* We're going to have to render the text a number
2678 * of times to show the progress marker on the text.
2680 var_SetBool( p_filter, "text-rerender", true );
2683 else if( !strcasecmp( "text", psz_node ) )
2689 /* Only text and karaoke tags are supported */
2690 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2691 xml_ReaderDelete( p_xml, p_xml_reader );
2692 p_xml_reader = NULL;
2704 uint32_t i_runs = 0;
2705 uint32_t i_k_runs = 0;
2706 uint32_t *pi_run_lengths = NULL;
2707 uint32_t *pi_k_run_lengths = NULL;
2708 uint32_t *pi_k_durations = NULL;
2709 ft_style_t **pp_styles = NULL;
2711 line_desc_t *p_lines = NULL;
2713 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2714 sizeof( uint32_t ) );
2719 rv = ProcessNodes( p_filter, p_xml_reader,
2720 p_region_in->p_style, psz_text, &i_len,
2721 &i_runs, &pi_run_lengths, &pp_styles,
2722 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2725 p_region_out->i_x = p_region_in->i_x;
2726 p_region_out->i_y = p_region_in->i_y;
2728 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2730 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2731 pi_run_lengths, pp_styles, &p_lines, &result,
2732 b_karaoke, i_k_runs, pi_k_run_lengths,
2736 for( k=0; k<i_runs; k++)
2737 DeleteStyle( pp_styles[k] );
2739 free( pi_run_lengths );
2742 /* Don't attempt to render text that couldn't be layed out
2745 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2747 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2749 Render( p_filter, p_region_out, p_lines,
2750 result.x, result.y );
2754 RenderYUVA( p_filter, p_region_out, p_lines,
2755 result.x, result.y );
2759 FreeLines( p_lines );
2761 xml_ReaderDelete( p_xml, p_xml_reader );
2763 xml_Delete( p_xml );
2765 stream_Delete( p_sub );
2771 static char* FontConfig_Select( FcConfig* priv, const char* family,
2772 bool b_bold, bool b_italic, int *i_idx )
2775 FcPattern *pat, *p_pat;
2779 pat = FcPatternCreate();
2780 if (!pat) return NULL;
2782 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2783 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2784 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2785 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2787 FcDefaultSubstitute( pat );
2789 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2791 FcPatternDestroy( pat );
2795 p_pat = FcFontMatch( priv, pat, &result );
2796 FcPatternDestroy( pat );
2797 if( !p_pat ) return NULL;
2799 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2800 || ( val_b != FcTrue ) )
2802 FcPatternDestroy( p_pat );
2805 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2810 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2812 FcPatternDestroy( p_pat );
2817 if( strcasecmp((const char*)val_s, family ) != 0 )
2818 msg_Warn( p_filter, "fontconfig: selected font family is not"
2819 "the requested one: '%s' != '%s'\n",
2820 (const char*)val_s, family );
2823 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2825 FcPatternDestroy( p_pat );
2829 FcPatternDestroy( p_pat );
2830 return strdup( (const char*)val_s );
2834 static void FreeLine( line_desc_t *p_line )
2837 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2839 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2841 free( p_line->pp_glyphs );
2842 free( p_line->p_glyph_pos );
2843 free( p_line->p_fg_rgb );
2844 free( p_line->p_bg_rgb );
2845 free( p_line->p_fg_bg_ratio );
2846 free( p_line->pi_underline_offset );
2847 free( p_line->pi_underline_thickness );
2851 static void FreeLines( line_desc_t *p_lines )
2853 line_desc_t *p_line, *p_next;
2855 if( !p_lines ) return;
2857 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2859 p_next = p_line->p_next;
2864 static line_desc_t *NewLine( int i_count )
2866 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2868 if( !p_line ) return NULL;
2869 p_line->i_height = 0;
2870 p_line->i_width = 0;
2871 p_line->p_next = NULL;
2873 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2874 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2875 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2876 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2877 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2878 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2879 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2880 if( ( p_line->pp_glyphs == NULL ) ||
2881 ( p_line->p_glyph_pos == NULL ) ||
2882 ( p_line->p_fg_rgb == NULL ) ||
2883 ( p_line->p_bg_rgb == NULL ) ||
2884 ( p_line->p_fg_bg_ratio == NULL ) ||
2885 ( p_line->pi_underline_offset == NULL ) ||
2886 ( p_line->pi_underline_thickness == NULL ) )
2888 free( p_line->pi_underline_thickness );
2889 free( p_line->pi_underline_offset );
2890 free( p_line->p_fg_rgb );
2891 free( p_line->p_bg_rgb );
2892 free( p_line->p_fg_bg_ratio );
2893 free( p_line->p_glyph_pos );
2894 free( p_line->pp_glyphs );
2898 p_line->pp_glyphs[0] = NULL;
2899 p_line->b_new_color_mode = false;
2904 static int GetFontSize( filter_t *p_filter )
2906 filter_sys_t *p_sys = p_filter->p_sys;
2910 if( p_sys->i_default_font_size )
2912 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2913 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2915 i_size = p_sys->i_default_font_size;
2919 var_Get( p_filter, "freetype-rel-fontsize", &val );
2920 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2921 p_filter->p_sys->i_display_height =
2922 p_filter->fmt_out.video.i_height;
2926 msg_Warn( p_filter, "invalid fontsize, using 12" );
2927 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2928 i_size = 12 * val.i_int / 1000;
2935 static int SetFontSize( filter_t *p_filter, int i_size )
2937 filter_sys_t *p_sys = p_filter->p_sys;
2941 i_size = GetFontSize( p_filter );
2943 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2946 p_sys->i_font_size = i_size;
2948 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2950 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2951 return VLC_EGENERIC;
2957 static void YUVFromRGB( uint32_t i_argb,
2958 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2960 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2961 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2962 int i_blue = ( i_argb & 0x000000ff );
2964 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2965 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2966 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2967 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2968 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2969 -585 * i_blue + 4096 + 1048576) >> 13, 240);