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 *****************************************************************************/
33 #include <vlc_block.h>
34 #include <vlc_filter.h>
35 #include <vlc_stream.h>
37 #include <vlc_input.h>
39 #ifdef HAVE_LINUX_LIMITS_H
40 # include <linux/limits.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 *,
93 vlc_bool_t, vlc_bool_t, int * );
94 static int CheckIfFontBuildComplete( filter_t *p_filter );
96 static line_desc_t *NewLine( int );
98 static int GetFontSize( filter_t *p_filter );
99 static int SetFontSize( filter_t *, int );
100 static void YUVFromRGB( uint32_t i_argb,
101 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
103 /*****************************************************************************
105 *****************************************************************************/
106 #define FONT_TEXT N_("Font")
107 #define FONT_LONGTEXT N_("Filename for the font you want to use")
108 #define FONTSIZE_TEXT N_("Font size in pixels")
109 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
110 "that will be rendered on the video. " \
111 "If set to something different than 0 this option will override the " \
112 "relative font size." )
113 #define OPACITY_TEXT N_("Opacity")
114 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
115 "text that will be rendered on the video. 0 = transparent, " \
116 "255 = totally opaque. " )
117 #define COLOR_TEXT N_("Text default color")
118 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
119 "the video. This must be an hexadecimal (like HTML colors). The first two "\
120 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
121 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
122 #define FONTSIZER_TEXT N_("Relative font size")
123 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
124 "fonts that will be rendered on the video. If absolute font size is set, "\
125 "relative size will be overriden." )
127 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
128 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
129 N_("Large"), N_("Larger") };
130 #define YUVP_TEXT N_("Use YUVP renderer")
131 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
132 "This option is only needed if you want to encode into DVB subtitles" )
133 #define EFFECT_TEXT N_("Font Effect")
134 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
135 "text to improve its readability." )
137 #define EFFECT_BACKGROUND 1
138 #define EFFECT_OUTLINE 2
139 #define EFFECT_OUTLINE_FAT 3
141 static int pi_effects[] = { 1, 2, 3 };
142 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
144 static int pi_color_values[] = {
145 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
146 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
147 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
149 static const char *ppsz_color_descriptions[] = {
150 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
151 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
152 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
155 set_shortname( _("Text renderer"));
156 set_description( _("Freetype2 font renderer") );
157 set_category( CAT_VIDEO );
158 set_subcategory( SUBCAT_VIDEO_SUBPIC );
160 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
164 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
165 FONTSIZE_LONGTEXT, VLC_TRUE );
168 /* opacity valid on 0..255, with default 255 = fully opaque */
169 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
170 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
173 /* hook to the color values list, with default 0x00ffffff = white */
174 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
175 COLOR_LONGTEXT, VLC_FALSE );
177 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
179 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
180 FONTSIZER_LONGTEXT, VLC_FALSE );
182 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
183 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
184 EFFECT_LONGTEXT, VLC_FALSE );
186 change_integer_list( pi_effects, ppsz_effects_text, 0 );
188 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
189 YUVP_LONGTEXT, VLC_TRUE );
191 set_capability( "text renderer", 100 );
192 add_shortcut( "text" );
193 set_callbacks( Create, Destroy );
198 /** NULL-terminated list of glyphs making the string */
199 FT_BitmapGlyph *pp_glyphs;
200 /** list of relative positions for the glyphs */
201 FT_Vector *p_glyph_pos;
202 /** list of RGB information for styled text
203 * -- if the rendering mode supports it (RenderYUVA) and
204 * b_new_color_mode is set, then it becomes possible to
205 * have multicoloured text within the subtitles. */
208 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
209 vlc_bool_t b_new_color_mode;
210 /** underline information -- only supplied if text should be underlined */
211 uint16_t *pi_underline_offset;
212 uint16_t *pi_underline_thickness;
216 int i_red, i_green, i_blue;
222 typedef struct font_stack_t font_stack_t;
227 uint32_t i_color; /* ARGB */
228 uint32_t i_karaoke_bg_color; /* ARGB */
230 font_stack_t *p_next;
236 uint32_t i_font_color; /* ARGB */
237 uint32_t i_karaoke_bg_color; /* ARGB */
240 vlc_bool_t b_underline;
244 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
245 static void FreeLines( line_desc_t * );
246 static void FreeLine( line_desc_t * );
247 #ifdef HAVE_FONTCONFIG
248 static void FontBuilder( vlc_object_t *p_this);
251 /*****************************************************************************
252 * filter_sys_t: freetype local data
253 *****************************************************************************
254 * This structure is part of the video output thread descriptor.
255 * It describes the freetype specific properties of an output thread.
256 *****************************************************************************/
259 FT_Library p_library; /* handle to library */
260 FT_Face p_face; /* handle to face object */
261 vlc_bool_t i_use_kerning;
262 uint8_t i_font_opacity;
267 int i_default_font_size;
268 int i_display_height;
269 #ifdef HAVE_FONTCONFIG
270 FcConfig *p_fontconfig;
271 vlc_bool_t b_fontconfig_ok;
272 vlc_mutex_t fontconfig_lock;
275 input_attachment_t **pp_font_attachments;
276 int i_font_attachments;
279 /*****************************************************************************
280 * Create: allocates osd-text video thread output method
281 *****************************************************************************
282 * This function allocates and initializes a Clone vout method.
283 *****************************************************************************/
284 static int Create( vlc_object_t *p_this )
286 filter_t *p_filter = (filter_t *)p_this;
288 char *psz_fontfile = NULL;
292 vlc_object_t *p_fontbuilder;
294 /* Allocate structure */
295 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
298 msg_Err( p_filter, "out of memory" );
302 p_sys->p_library = 0;
303 p_sys->i_font_size = 0;
304 p_sys->i_display_height = 0;
306 var_Create( p_filter, "freetype-font",
307 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
308 var_Create( p_filter, "freetype-fontsize",
309 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
310 var_Create( p_filter, "freetype-rel-fontsize",
311 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
312 var_Create( p_filter, "freetype-opacity",
313 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
314 var_Create( p_filter, "freetype-effect",
315 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
316 var_Get( p_filter, "freetype-opacity", &val );
317 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
318 var_Create( p_filter, "freetype-color",
319 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
320 var_Get( p_filter, "freetype-color", &val );
321 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
322 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
324 /* Look what method was requested */
325 var_Get( p_filter, "freetype-font", &val );
326 psz_fontfile = val.psz_string;
327 if( !psz_fontfile || !*psz_fontfile )
329 if( psz_fontfile ) free( psz_fontfile );
330 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
333 msg_Err( p_filter, "out of memory" );
337 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
338 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
339 #elif defined(__APPLE__)
340 strcpy( psz_fontfile, DEFAULT_FONT );
342 msg_Err( p_filter, "user didn't specify a font" );
347 i_error = FT_Init_FreeType( &p_sys->p_library );
350 msg_Err( p_filter, "couldn't initialize freetype" );
353 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
355 if( i_error == FT_Err_Unknown_File_Format )
357 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
362 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
366 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
369 msg_Err( p_filter, "font has no unicode translation table" );
373 #ifdef HAVE_FONTCONFIG
374 vlc_mutex_init( p_filter, &p_sys->fontconfig_lock );
375 p_sys->b_fontconfig_ok = VLC_FALSE;
376 p_sys->p_fontconfig = NULL;
378 /* Check for an existing Fontbuilder thread */
379 lock = var_AcquireMutex( "fontbuilder" );
380 p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
384 if( ! p_fontbuilder )
386 /* Create the FontBuilder thread as a child of a top-level
387 * object, so that it can survive the destruction of the
388 * freetype object - the fontlist only needs to be built once,
389 * and calling the fontbuild a second time while the first is
390 * still in progress can cause thread instabilities.
393 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
394 VLC_OBJECT_GENERIC );
397 p_fontbuilder->psz_object_name = "fontlist builder";
398 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
400 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
401 var_SetBool( p_fontbuilder, "build-done", VLC_FALSE );
403 if( vlc_thread_create( p_fontbuilder,
406 VLC_THREAD_PRIORITY_LOW,
409 /* Don't destroy the fontconfig object - we won't be able to do
410 * italics or bold or change the font face, but we will still
411 * be able to do underline and change the font size.
413 msg_Warn( p_filter, "fontconfig database builder thread can't "
414 "be launched. Font styling support will be limited." );
419 vlc_object_destroy( p_fontbuilder );
424 vlc_object_release( p_fontbuilder );
426 vlc_mutex_unlock( lock );
430 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
432 var_Get( p_filter, "freetype-fontsize", &val );
433 p_sys->i_default_font_size = val.i_int;
434 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
436 if( psz_fontfile ) free( psz_fontfile );
438 p_sys->pp_font_attachments = NULL;
439 p_sys->i_font_attachments = 0;
441 p_filter->pf_render_text = RenderText;
442 #ifdef HAVE_FONTCONFIG
443 p_filter->pf_render_html = RenderHtml;
445 p_filter->pf_render_html = NULL;
448 LoadFontsFromAttachments( p_filter );
453 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
454 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
455 if( psz_fontfile ) free( psz_fontfile );
460 /*****************************************************************************
461 * Destroy: destroy Clone video thread output method
462 *****************************************************************************
463 * Clean up all data and library connections
464 *****************************************************************************/
465 static void Destroy( vlc_object_t *p_this )
467 filter_t *p_filter = (filter_t *)p_this;
468 filter_sys_t *p_sys = p_filter->p_sys;
470 if( p_sys->pp_font_attachments )
474 for( k = 0; k < p_sys->i_font_attachments; k++ )
476 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
479 free( p_sys->pp_font_attachments );
482 #ifdef HAVE_FONTCONFIG
483 vlc_mutex_destroy( &p_sys->fontconfig_lock );
485 if( p_sys->p_fontconfig )
487 FcConfigDestroy( p_sys->p_fontconfig );
488 p_sys->p_fontconfig = NULL;
490 /* FcFini asserts calling the subfunction FcCacheFini()
491 * even if no other library functions have been made since FcInit(),
492 * so don't call it. */
494 FT_Done_Face( p_sys->p_face );
495 FT_Done_FreeType( p_sys->p_library );
499 #ifdef HAVE_FONTCONFIG
501 static void FontBuilder( vlc_object_t *p_this )
503 FcConfig *p_fontconfig = FcInitLoadConfig();
506 vlc_thread_ready( p_this );
512 msg_Dbg( p_this, "Building font database..." );
514 if(! FcConfigBuildFonts( p_fontconfig ))
516 /* Don't destroy the fontconfig object - we won't be able to do
517 * italics or bold or change the font face, but we will still
518 * be able to do underline and change the font size.
520 msg_Err( p_this, "fontconfig database can't be built. "
521 "Font styling won't be available" );
525 msg_Dbg( p_this, "Finished building font database." );
526 if( t1 > 0 && t2 > 0 )
527 msg_Dbg( p_this, "Took %ld seconds", t2 - t1 );
529 lock = var_AcquireMutex( "fontbuilder" );
530 var_SetBool( p_this, "build-done", VLC_TRUE );
532 FcConfigDestroy( p_fontconfig );
533 vlc_mutex_unlock( lock );
539 /*****************************************************************************
540 * Make any TTF/OTF fonts present in the attachments of the media file
541 * and store them for later use by the FreeType Engine
542 *****************************************************************************/
543 static int LoadFontsFromAttachments( filter_t *p_filter )
545 filter_sys_t *p_sys = p_filter->p_sys;
546 input_thread_t *p_input;
547 input_attachment_t **pp_attachments;
548 int i_attachments_cnt;
550 int rv = VLC_SUCCESS;
552 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
556 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
558 vlc_object_release(p_input);
562 p_sys->i_font_attachments = 0;
563 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
564 if(! p_sys->pp_font_attachments )
567 for( k = 0; k < i_attachments_cnt; k++ )
569 input_attachment_t *p_attach = pp_attachments[k];
571 if( p_sys->pp_font_attachments )
573 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
574 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
575 ( p_attach->i_data > 0 ) &&
576 ( p_attach->p_data != NULL ) )
578 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
582 vlc_input_attachment_Delete( p_attach );
587 vlc_input_attachment_Delete( p_attach );
590 free( pp_attachments );
592 vlc_object_release(p_input);
597 /*****************************************************************************
598 * Render: place string in picture
599 *****************************************************************************
600 * This function merges the previously rendered freetype glyphs into a picture
601 *****************************************************************************/
602 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
603 line_desc_t *p_line, int i_width, int i_height )
605 static uint8_t pi_gamma[16] =
606 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
607 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
611 int i, x, y, i_pitch;
612 uint8_t i_y; /* YUV values, derived from incoming RGB */
614 subpicture_region_t *p_region_tmp;
616 /* Create a new subpicture region */
617 memset( &fmt, 0, sizeof(video_format_t) );
618 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
620 fmt.i_width = fmt.i_visible_width = i_width + 4;
621 fmt.i_height = fmt.i_visible_height = i_height + 4;
622 if( p_region->fmt.i_visible_width > 0 )
623 fmt.i_visible_width = p_region->fmt.i_visible_width;
624 if( p_region->fmt.i_visible_height > 0 )
625 fmt.i_visible_height = p_region->fmt.i_visible_height;
626 fmt.i_x_offset = fmt.i_y_offset = 0;
627 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
630 msg_Err( p_filter, "cannot allocate SPU region" );
634 p_region->fmt = p_region_tmp->fmt;
635 p_region->picture = p_region_tmp->picture;
636 free( p_region_tmp );
638 /* Calculate text color components */
639 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
640 25 * p_line->i_blue + 128) >> 8) + 16;
641 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
642 112 * p_line->i_blue + 128) >> 8) + 128;
643 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
644 18 * p_line->i_blue + 128) >> 8) + 128;
647 fmt.p_palette->i_entries = 16;
648 for( i = 0; i < 8; i++ )
650 fmt.p_palette->palette[i][0] = 0;
651 fmt.p_palette->palette[i][1] = 0x80;
652 fmt.p_palette->palette[i][2] = 0x80;
653 fmt.p_palette->palette[i][3] = pi_gamma[i];
654 fmt.p_palette->palette[i][3] =
655 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
657 for( i = 8; i < fmt.p_palette->i_entries; i++ )
659 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
660 fmt.p_palette->palette[i][1] = i_u;
661 fmt.p_palette->palette[i][2] = i_v;
662 fmt.p_palette->palette[i][3] = pi_gamma[i];
663 fmt.p_palette->palette[i][3] =
664 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
667 p_dst = p_region->picture.Y_PIXELS;
668 i_pitch = p_region->picture.Y_PITCH;
670 /* Initialize the region pixels */
671 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
673 for( ; p_line != NULL; p_line = p_line->p_next )
675 int i_glyph_tmax = 0;
676 int i_bitmap_offset, i_offset, i_align_offset = 0;
677 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
679 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
680 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
683 if( p_line->i_width < i_width )
685 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
687 i_align_offset = i_width - p_line->i_width;
689 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
691 i_align_offset = ( i_width - p_line->i_width ) / 2;
695 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
697 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
699 i_offset = ( p_line->p_glyph_pos[ i ].y +
700 i_glyph_tmax - p_glyph->top + 2 ) *
701 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
704 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
706 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
708 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
710 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
717 /* Outlining (find something better than nearest neighbour filtering ?) */
720 uint8_t *p_dst = p_region->picture.Y_PIXELS;
721 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
722 uint8_t left, current;
724 for( y = 1; y < (int)fmt.i_height - 1; y++ )
726 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
727 p_dst += p_region->picture.Y_PITCH;
730 for( x = 1; x < (int)fmt.i_width - 1; x++ )
733 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
734 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;
738 memset( p_top, 0, fmt.i_width );
744 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
745 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
746 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
747 int i_glyph_tmax, int i_align_offset,
748 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
749 subpicture_region_t *p_region)
753 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
755 p_dst_y = p_region->picture.Y_PIXELS;
756 p_dst_u = p_region->picture.U_PIXELS;
757 p_dst_v = p_region->picture.V_PIXELS;
758 p_dst_a = p_region->picture.A_PIXELS;
759 i_pitch = p_region->picture.A_PITCH;
761 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
762 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
764 for( y = 0; y < i_line_thickness; y++ )
766 int i_extra = p_this_glyph->bitmap.width;
770 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
771 (p_this_glyph_pos->x + p_this_glyph->left);
773 for( x = 0; x < i_extra; x++ )
775 vlc_bool_t b_ok = VLC_TRUE;
777 /* break the underline around the tails of any glyphs which cross it */
778 for( z = x - i_line_thickness;
779 z < x + i_line_thickness && b_ok;
782 if( p_next_glyph && ( z >= i_extra ) )
784 int i_row = i_line_offset + p_next_glyph->top + y;
786 if( ( p_next_glyph->bitmap.rows > i_row ) &&
787 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
792 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
794 int i_row = i_line_offset + p_this_glyph->top + y;
796 if( ( p_this_glyph->bitmap.rows > i_row ) &&
797 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
806 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
807 p_dst_u[i_offset+x] = i_u;
808 p_dst_v[i_offset+x] = i_v;
809 p_dst_a[i_offset+x] = 255;
816 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
818 uint8_t *p_dst = p_region->picture.A_PIXELS;
819 int i_pitch = p_region->picture.A_PITCH;
822 for( ; p_line != NULL; p_line = p_line->p_next )
824 int i_glyph_tmax=0, i = 0;
825 int i_bitmap_offset, i_offset, i_align_offset = 0;
826 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
828 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
829 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
832 if( p_line->i_width < i_width )
834 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
836 i_align_offset = i_width - p_line->i_width;
838 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
840 i_align_offset = ( i_width - p_line->i_width ) / 2;
844 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
846 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
848 i_offset = ( p_line->p_glyph_pos[ i ].y +
849 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
850 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
851 i_align_offset +xoffset;
853 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
855 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
857 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
858 if( p_dst[i_offset+x] <
859 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
861 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
870 /*****************************************************************************
871 * Render: place string in picture
872 *****************************************************************************
873 * This function merges the previously rendered freetype glyphs into a picture
874 *****************************************************************************/
875 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
876 line_desc_t *p_line, int i_width, int i_height )
878 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
880 int i, x, y, i_pitch, i_alpha;
881 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
882 subpicture_region_t *p_region_tmp;
884 if( i_width == 0 || i_height == 0 )
887 /* Create a new subpicture region */
888 memset( &fmt, 0, sizeof(video_format_t) );
889 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
891 fmt.i_width = fmt.i_visible_width = i_width + 6;
892 fmt.i_height = fmt.i_visible_height = i_height + 6;
893 if( p_region->fmt.i_visible_width > 0 )
894 fmt.i_visible_width = p_region->fmt.i_visible_width;
895 if( p_region->fmt.i_visible_height > 0 )
896 fmt.i_visible_height = p_region->fmt.i_visible_height;
897 fmt.i_x_offset = fmt.i_y_offset = 0;
898 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
901 msg_Err( p_filter, "cannot allocate SPU region" );
905 p_region->fmt = p_region_tmp->fmt;
906 p_region->picture = p_region_tmp->picture;
907 free( p_region_tmp );
909 /* Calculate text color components */
910 YUVFromRGB( (p_line->i_red << 16) |
911 (p_line->i_green << 8) |
914 i_alpha = p_line->i_alpha;
916 p_dst_y = p_region->picture.Y_PIXELS;
917 p_dst_u = p_region->picture.U_PIXELS;
918 p_dst_v = p_region->picture.V_PIXELS;
919 p_dst_a = p_region->picture.A_PIXELS;
920 i_pitch = p_region->picture.A_PITCH;
922 /* Initialize the region pixels */
923 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
925 memset( p_dst_y, 0x00, 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, 0, i_pitch * p_region->fmt.i_height );
932 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
933 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
934 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
935 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
937 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
938 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
940 DrawBlack( p_line, i_width, p_region, 0, 0);
941 DrawBlack( p_line, i_width, p_region, -1, 0);
942 DrawBlack( p_line, i_width, p_region, 0, -1);
943 DrawBlack( p_line, i_width, p_region, 1, 0);
944 DrawBlack( p_line, i_width, p_region, 0, 1);
947 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
949 DrawBlack( p_line, i_width, p_region, -1, -1);
950 DrawBlack( p_line, i_width, p_region, -1, 1);
951 DrawBlack( p_line, i_width, p_region, 1, -1);
952 DrawBlack( p_line, i_width, p_region, 1, 1);
954 DrawBlack( p_line, i_width, p_region, -2, 0);
955 DrawBlack( p_line, i_width, p_region, 0, -2);
956 DrawBlack( p_line, i_width, p_region, 2, 0);
957 DrawBlack( p_line, i_width, p_region, 0, 2);
959 DrawBlack( p_line, i_width, p_region, -2, -2);
960 DrawBlack( p_line, i_width, p_region, -2, 2);
961 DrawBlack( p_line, i_width, p_region, 2, -2);
962 DrawBlack( p_line, i_width, p_region, 2, 2);
964 DrawBlack( p_line, i_width, p_region, -3, 0);
965 DrawBlack( p_line, i_width, p_region, 0, -3);
966 DrawBlack( p_line, i_width, p_region, 3, 0);
967 DrawBlack( p_line, i_width, p_region, 0, 3);
970 for( ; p_line != NULL; p_line = p_line->p_next )
972 int i_glyph_tmax = 0;
973 int i_bitmap_offset, i_offset, i_align_offset = 0;
974 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
976 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
977 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
980 if( p_line->i_width < i_width )
982 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
984 i_align_offset = i_width - p_line->i_width;
986 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
988 i_align_offset = ( i_width - p_line->i_width ) / 2;
992 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
994 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
996 i_offset = ( p_line->p_glyph_pos[ i ].y +
997 i_glyph_tmax - p_glyph->top + 3 ) *
998 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1001 if( p_line->b_new_color_mode )
1003 /* Every glyph can (and in fact must) have its own color */
1004 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1007 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1009 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1011 uint8_t i_y_local = i_y;
1012 uint8_t i_u_local = i_u;
1013 uint8_t i_v_local = i_v;
1015 if( p_line->p_fg_bg_ratio != 0x00 )
1017 int i_split = p_glyph->bitmap.width *
1018 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1022 YUVFromRGB( p_line->p_bg_rgb[ i ],
1023 &i_y_local, &i_u_local, &i_v_local );
1027 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1029 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1030 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1032 p_dst_u[i_offset+x] = i_u;
1033 p_dst_v[i_offset+x] = i_v;
1035 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1036 p_dst_a[i_offset+x] = 0xff;
1039 i_offset += i_pitch;
1042 if( p_line->pi_underline_thickness[ i ] )
1044 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1045 p_line->pi_underline_offset[ i ],
1046 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1047 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1048 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1049 i_glyph_tmax, i_align_offset,
1050 i_y, i_u, i_v, i_alpha,
1056 /* Apply the alpha setting */
1057 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1058 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1064 * This function renders a text subpicture region into another one.
1065 * It also calculates the size needed for this string, and renders the
1066 * needed glyphs into memory. It is used as pf_add_string callback in
1067 * the vout method by this module
1069 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1070 subpicture_region_t *p_region_in )
1072 filter_sys_t *p_sys = p_filter->p_sys;
1073 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1074 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1075 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1076 int i_string_length;
1078 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1079 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1089 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1090 psz_string = p_region_in->psz_text;
1091 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1093 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1094 i_scale = val.i_int;
1096 if( p_region_in->p_style )
1098 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1099 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1100 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1104 i_font_color = p_sys->i_font_color;
1105 i_font_alpha = 255 - p_sys->i_font_opacity;
1106 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1109 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1110 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1111 SetFontSize( p_filter, i_font_size );
1113 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1114 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1115 i_blue = i_font_color & 0x000000FF;
1117 result.x = result.y = 0;
1118 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1120 psz_unicode = psz_unicode_orig =
1121 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1122 if( psz_unicode == NULL )
1124 msg_Err( p_filter, "out of memory" );
1127 #if defined(WORDS_BIGENDIAN)
1128 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1130 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1132 if( iconv_handle == (vlc_iconv_t)-1 )
1134 msg_Warn( p_filter, "unable to do conversion" );
1140 const char *p_in_buffer = psz_string;
1141 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1142 i_in_bytes = strlen( psz_string );
1143 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1144 i_out_bytes_left = i_out_bytes;
1145 p_out_buffer = (char *)psz_unicode;
1146 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1147 &p_out_buffer, &i_out_bytes_left );
1149 vlc_iconv_close( iconv_handle );
1153 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1154 "bytes left %u", (unsigned)i_in_bytes );
1157 *(uint32_t*)p_out_buffer = 0;
1158 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1161 #if defined(HAVE_FRIBIDI)
1163 uint32_t *p_fribidi_string;
1164 int start_pos, pos = 0;
1166 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1167 if( !p_fribidi_string )
1169 msg_Err( p_filter, "out of memory" );
1173 /* Do bidi conversion line-by-line */
1174 while(pos < i_string_length)
1176 while(pos < i_string_length) {
1177 i_char = psz_unicode[pos];
1178 if (i_char != '\r' && i_char != '\n')
1180 p_fribidi_string[pos] = i_char;
1184 while(pos < i_string_length) {
1185 i_char = psz_unicode[pos];
1186 if (i_char == '\r' || i_char == '\n')
1190 if (pos > start_pos)
1192 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1193 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
1194 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
1198 free( psz_unicode_orig );
1199 psz_unicode = psz_unicode_orig = p_fribidi_string;
1200 p_fribidi_string[ i_string_length ] = 0;
1204 /* Calculate relative glyph positions and a bounding box for the
1206 if( !(p_line = NewLine( strlen( psz_string ))) )
1208 msg_Err( p_filter, "out of memory" );
1212 i_pen_x = i_pen_y = 0;
1214 psz_line_start = psz_unicode;
1216 #define face p_sys->p_face
1217 #define glyph face->glyph
1219 while( *psz_unicode )
1221 i_char = *psz_unicode++;
1222 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1227 if( i_char == '\n' )
1229 psz_line_start = psz_unicode;
1230 if( !(p_next = NewLine( strlen( psz_string ))) )
1232 msg_Err( p_filter, "out of memory" );
1235 p_line->p_next = p_next;
1236 p_line->i_width = line.xMax;
1237 p_line->i_height = face->size->metrics.height >> 6;
1238 p_line->pp_glyphs[ i ] = NULL;
1239 p_line->i_alpha = i_font_alpha;
1240 p_line->i_red = i_red;
1241 p_line->i_green = i_green;
1242 p_line->i_blue = i_blue;
1245 result.x = __MAX( result.x, line.xMax );
1246 result.y += face->size->metrics.height >> 6;
1249 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1250 i_pen_y += face->size->metrics.height >> 6;
1252 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1257 i_glyph_index = FT_Get_Char_Index( face, i_char );
1258 if( p_sys->i_use_kerning && i_glyph_index
1262 FT_Get_Kerning( face, i_previous, i_glyph_index,
1263 ft_kerning_default, &delta );
1264 i_pen_x += delta.x >> 6;
1267 p_line->p_glyph_pos[ i ].x = i_pen_x;
1268 p_line->p_glyph_pos[ i ].y = i_pen_y;
1269 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1272 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1276 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1279 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1283 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1284 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1287 FT_Done_Glyph( tmp_glyph );
1290 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1293 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1294 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1295 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1297 p_line->pp_glyphs[ i ] = NULL;
1299 p_line = NewLine( strlen( psz_string ));
1300 if( p_prev ) p_prev->p_next = p_line;
1301 else p_lines = p_line;
1303 uint32_t *psz_unicode_saved = psz_unicode;
1304 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1308 if( psz_unicode == psz_line_start )
1309 { /* try harder to break that line */
1310 psz_unicode = psz_unicode_saved;
1311 while( psz_unicode > psz_line_start &&
1312 *psz_unicode != '_' && *psz_unicode != '/' &&
1313 *psz_unicode != '\\' && *psz_unicode != '.' )
1318 if( psz_unicode == psz_line_start )
1320 msg_Warn( p_filter, "unbreakable string" );
1325 *psz_unicode = '\n';
1327 psz_unicode = psz_line_start;
1330 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1333 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1334 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1336 i_previous = i_glyph_index;
1337 i_pen_x += glyph->advance.x >> 6;
1341 p_line->i_width = line.xMax;
1342 p_line->i_height = face->size->metrics.height >> 6;
1343 p_line->pp_glyphs[ i ] = NULL;
1344 p_line->i_alpha = i_font_alpha;
1345 p_line->i_red = i_red;
1346 p_line->i_green = i_green;
1347 p_line->i_blue = i_blue;
1348 result.x = __MAX( result.x, line.xMax );
1349 result.y += line.yMax - line.yMin;
1354 p_region_out->i_x = p_region_in->i_x;
1355 p_region_out->i_y = p_region_in->i_y;
1357 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1358 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1360 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1362 if( psz_unicode_orig ) free( psz_unicode_orig );
1363 FreeLines( p_lines );
1367 if( psz_unicode_orig ) free( psz_unicode_orig );
1368 FreeLines( p_lines );
1369 return VLC_EGENERIC;
1372 #ifdef HAVE_FONTCONFIG
1373 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1374 uint32_t i_font_color, uint32_t i_karaoke_bg_color, vlc_bool_t b_bold,
1375 vlc_bool_t b_italic, vlc_bool_t b_uline )
1377 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1381 p_style->i_font_size = i_font_size;
1382 p_style->i_font_color = i_font_color;
1383 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1384 p_style->b_italic = b_italic;
1385 p_style->b_bold = b_bold;
1386 p_style->b_underline = b_uline;
1388 p_style->psz_fontname = strdup( psz_fontname );
1393 static void DeleteStyle( ft_style_t *p_style )
1397 if( p_style->psz_fontname )
1398 free( p_style->psz_fontname );
1403 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1410 if(( s1->i_font_size == s2->i_font_size ) &&
1411 ( s1->i_font_color == s2->i_font_color ) &&
1412 ( s1->b_italic == s2->b_italic ) &&
1413 ( s1->b_bold == s2->b_bold ) &&
1414 ( s1->b_underline == s2->b_underline ) &&
1415 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1422 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1423 uint32_t i_color, uint32_t i_karaoke_bg_color )
1425 font_stack_t *p_new;
1428 return VLC_EGENERIC;
1430 p_new = malloc( sizeof( font_stack_t ) );
1434 p_new->p_next = NULL;
1437 p_new->psz_name = strdup( psz_name );
1439 p_new->psz_name = NULL;
1441 p_new->i_size = i_size;
1442 p_new->i_color = i_color;
1443 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1451 font_stack_t *p_last;
1453 for( p_last = *p_font;
1455 p_last = p_last->p_next )
1458 p_last->p_next = p_new;
1463 static int PopFont( font_stack_t **p_font )
1465 font_stack_t *p_last, *p_next_to_last;
1467 if( !p_font || !*p_font )
1468 return VLC_EGENERIC;
1470 p_next_to_last = NULL;
1471 for( p_last = *p_font;
1473 p_last = p_last->p_next )
1475 p_next_to_last = p_last;
1478 if( p_next_to_last )
1479 p_next_to_last->p_next = NULL;
1483 free( p_last->psz_name );
1489 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1490 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1492 font_stack_t *p_last;
1494 if( !p_font || !*p_font )
1495 return VLC_EGENERIC;
1497 for( p_last=*p_font;
1499 p_last=p_last->p_next )
1502 *psz_name = p_last->psz_name;
1503 *i_size = p_last->i_size;
1504 *i_color = p_last->i_color;
1505 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1510 static void IconvText( filter_t *p_filter, const char *psz_string,
1511 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1513 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1515 /* If memory hasn't been allocated for our output string, allocate it here
1516 * - the calling function must now be responsible for freeing it.
1518 if( !*ppsz_unicode )
1519 *ppsz_unicode = (uint32_t *)
1520 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1522 /* We don't need to handle a NULL pointer in *ppsz_unicode
1523 * if we are instead testing for a non NULL value like we are here */
1527 #if defined(WORDS_BIGENDIAN)
1528 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1530 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1532 if( iconv_handle != (vlc_iconv_t)-1 )
1534 char *p_in_buffer, *p_out_buffer;
1535 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1536 i_in_bytes = strlen( psz_string );
1537 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1538 i_out_bytes_left = i_out_bytes;
1539 p_in_buffer = (char *) psz_string;
1540 p_out_buffer = (char *) *ppsz_unicode;
1541 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1542 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1544 vlc_iconv_close( iconv_handle );
1548 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1549 "bytes left %u", (unsigned)i_in_bytes );
1553 *(uint32_t*)p_out_buffer = 0;
1555 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1560 msg_Warn( p_filter, "unable to do conversion" );
1565 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1566 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1567 vlc_bool_t b_uline )
1569 ft_style_t *p_style = NULL;
1571 char *psz_fontname = NULL;
1572 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1573 uint32_t i_karaoke_bg_color = i_font_color;
1574 int i_font_size = p_sys->i_font_size;
1576 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1577 &i_font_color, &i_karaoke_bg_color ))
1579 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1580 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1585 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1586 vlc_bool_t b_uline, int i_karaoke_bgcolor,
1587 line_desc_t *p_line, uint32_t *psz_unicode,
1588 int *pi_pen_x, int i_pen_y, int *pi_start,
1589 FT_Vector *p_result )
1594 vlc_bool_t b_first_on_line = VLC_TRUE;
1597 int i_pen_x_start = *pi_pen_x;
1599 uint32_t *psz_unicode_start = psz_unicode;
1601 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1603 /* Account for part of line already in position */
1604 for( i=0; i<*pi_start; i++ )
1608 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1609 ft_glyph_bbox_pixels, &glyph_size );
1611 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1612 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1613 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1614 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1620 b_first_on_line = VLC_FALSE;
1622 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1628 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1629 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1633 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1634 ft_kerning_default, &delta );
1635 *pi_pen_x += delta.x >> 6;
1637 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1638 p_line->p_glyph_pos[ i ].y = i_pen_y;
1640 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1644 "unable to render text FT_Load_Glyph returned %d", i_error );
1645 p_line->pp_glyphs[ i ] = NULL;
1646 return VLC_EGENERIC;
1648 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1652 "unable to render text FT_Get_Glyph returned %d", i_error );
1653 p_line->pp_glyphs[ i ] = NULL;
1654 return VLC_EGENERIC;
1656 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1657 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1660 FT_Done_Glyph( tmp_glyph );
1665 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1666 p_face->size->metrics.y_scale));
1667 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1668 p_face->size->metrics.y_scale));
1670 p_line->pi_underline_offset[ i ] =
1671 ( aOffset < 0 ) ? -aOffset : aOffset;
1672 p_line->pi_underline_thickness[ i ] =
1673 ( aSize < 0 ) ? -aSize : aSize;
1675 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1676 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1677 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1678 p_line->p_fg_bg_ratio[ i ] = 0x00;
1680 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1681 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1682 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1684 while( --i > *pi_start )
1686 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1689 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1693 if( psz_unicode == psz_unicode_start )
1695 if( b_first_on_line )
1697 msg_Warn( p_filter, "unbreakable string" );
1698 p_line->pp_glyphs[ i ] = NULL;
1699 return VLC_EGENERIC;
1701 *pi_pen_x = i_pen_x_start;
1703 p_line->i_width = line.xMax;
1704 p_line->i_height = __MAX( p_line->i_height,
1705 p_face->size->metrics.height >> 6 );
1706 p_line->pp_glyphs[ i ] = NULL;
1708 p_result->x = __MAX( p_result->x, line.xMax );
1709 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1710 i_yMax - i_yMin ) );
1717 *psz_unicode = '\n';
1719 psz_unicode = psz_unicode_start;
1720 *pi_pen_x = i_pen_x_start;
1728 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1729 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1731 i_previous = i_glyph_index;
1732 *pi_pen_x += p_face->glyph->advance.x >> 6;
1735 p_line->i_width = line.xMax;
1736 p_line->i_height = __MAX( p_line->i_height,
1737 p_face->size->metrics.height >> 6 );
1738 p_line->pp_glyphs[ i ] = NULL;
1740 p_result->x = __MAX( p_result->x, line.xMax );
1741 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1742 line.yMax - line.yMin ) );
1746 /* Get rid of any text processed - if necessary repositioning
1747 * at the start of a new line of text
1751 *psz_unicode_start = '\0';
1753 else if( psz_unicode > psz_unicode_start )
1755 for( i=0; psz_unicode[ i ]; i++ )
1756 psz_unicode_start[ i ] = psz_unicode[ i ];
1757 psz_unicode_start[ i ] = '\0';
1763 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1764 font_stack_t **p_fonts, int i_scale )
1767 char *psz_fontname = NULL;
1768 uint32_t i_font_color = 0xffffff;
1769 int i_font_alpha = 0;
1770 uint32_t i_karaoke_bg_color = 0x00ffffff;
1771 int i_font_size = 24;
1773 /* Default all attributes to the top font in the stack -- in case not
1774 * all attributes are specified in the sub-font
1776 if( VLC_SUCCESS == PeekFont( p_fonts,
1780 &i_karaoke_bg_color ))
1782 psz_fontname = strdup( psz_fontname );
1783 i_font_size = i_font_size * 1000 / i_scale;
1785 i_font_alpha = (i_font_color >> 24) & 0xff;
1786 i_font_color &= 0x00ffffff;
1788 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1790 char *psz_name = xml_ReaderName( p_xml_reader );
1791 char *psz_value = xml_ReaderValue( p_xml_reader );
1793 if( psz_name && psz_value )
1795 if( !strcasecmp( "face", psz_name ) )
1797 if( psz_fontname ) free( psz_fontname );
1798 psz_fontname = strdup( psz_value );
1800 else if( !strcasecmp( "size", psz_name ) )
1802 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1804 int i_value = atoi( psz_value );
1806 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1807 i_font_size += ( i_value * i_font_size ) / 10;
1808 else if( i_value < -5 )
1809 i_font_size = - i_value;
1810 else if( i_value > 5 )
1811 i_font_size = i_value;
1814 i_font_size = atoi( psz_value );
1816 else if( !strcasecmp( "color", psz_name ) &&
1817 ( psz_value[0] == '#' ) )
1819 i_font_color = strtol( psz_value + 1, NULL, 16 );
1820 i_font_color &= 0x00ffffff;
1822 else if( !strcasecmp( "alpha", psz_name ) &&
1823 ( psz_value[0] == '#' ) )
1825 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1826 i_font_alpha &= 0xff;
1832 rv = PushFont( p_fonts,
1834 i_font_size * i_scale / 1000,
1835 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1836 i_karaoke_bg_color );
1838 free( psz_fontname );
1843 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1844 uint32_t **psz_text_out, uint32_t *pi_runs,
1845 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1846 ft_style_t *p_style )
1848 uint32_t i_string_length = 0;
1850 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1851 *psz_text_out += i_string_length;
1853 if( ppp_styles && ppi_run_lengths )
1859 *ppp_styles = (ft_style_t **)
1860 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1862 else if( *pi_runs == 1 )
1864 *ppp_styles = (ft_style_t **)
1865 malloc( *pi_runs * sizeof( ft_style_t * ) );
1868 /* We have just malloc'ed this memory successfully -
1869 * *pi_runs HAS to be within the memory area of *ppp_styles */
1872 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1876 if( *ppi_run_lengths )
1878 *ppi_run_lengths = (uint32_t *)
1879 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1881 else if( *pi_runs == 1 )
1883 *ppi_run_lengths = (uint32_t *)
1884 malloc( *pi_runs * sizeof( uint32_t ) );
1887 /* same remarks here */
1888 if( *ppi_run_lengths )
1890 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1893 /* If we couldn't use the p_style argument due to memory allocation
1894 * problems above, release it here.
1896 if( p_style ) DeleteStyle( p_style );
1899 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1900 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1902 /* Karaoke tags _PRECEDE_ the text they specify a duration
1903 * for, therefore we are working out the length for the
1904 * previous tag, and first time through we have nothing
1906 if( pi_k_run_lengths )
1911 /* Work out how many characters are presently in the string
1913 for( i = 0; i < i_runs; i++ )
1914 i_chars += pi_run_lengths[ i ];
1916 /* Subtract away those we've already allocated to other
1919 for( i = 0; i < i_k_runs; i++ )
1920 i_chars -= pi_k_run_lengths[ i ];
1922 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1926 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1927 uint32_t **ppi_k_run_lengths,
1928 uint32_t **ppi_k_durations )
1930 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1932 char *psz_name = xml_ReaderName( p_xml_reader );
1933 char *psz_value = xml_ReaderValue( p_xml_reader );
1935 if( psz_name && psz_value &&
1936 !strcasecmp( "t", psz_name ) )
1938 if( ppi_k_durations && ppi_k_run_lengths )
1942 if( *ppi_k_durations )
1944 *ppi_k_durations = (uint32_t *)
1945 realloc( *ppi_k_durations,
1946 *pi_k_runs * sizeof( uint32_t ) );
1948 else if( *pi_k_runs == 1 )
1950 *ppi_k_durations = (uint32_t *)
1951 malloc( *pi_k_runs * sizeof( uint32_t ) );
1954 if( *ppi_k_run_lengths )
1956 *ppi_k_run_lengths = (uint32_t *)
1957 realloc( *ppi_k_run_lengths,
1958 *pi_k_runs * sizeof( uint32_t ) );
1960 else if( *pi_k_runs == 1 )
1962 *ppi_k_run_lengths = (uint32_t *)
1963 malloc( *pi_k_runs * sizeof( uint32_t ) );
1965 if( *ppi_k_durations )
1966 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1968 if( *ppi_k_run_lengths )
1969 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1972 if( psz_name ) free( psz_name );
1973 if( psz_value ) free( psz_value );
1977 static int ProcessNodes( filter_t *p_filter,
1978 xml_reader_t *p_xml_reader,
1979 text_style_t *p_font_style,
1984 uint32_t **ppi_run_lengths,
1985 ft_style_t ***ppp_styles,
1987 vlc_bool_t b_karaoke,
1988 uint32_t *pi_k_runs,
1989 uint32_t **ppi_k_run_lengths,
1990 uint32_t **ppi_k_durations )
1992 int rv = VLC_SUCCESS;
1993 filter_sys_t *p_sys = p_filter->p_sys;
1994 uint32_t *psz_text_orig = psz_text;
1995 font_stack_t *p_fonts = NULL;
1999 char *psz_node = NULL;
2001 vlc_bool_t b_italic = VLC_FALSE;
2002 vlc_bool_t b_bold = VLC_FALSE;
2003 vlc_bool_t b_uline = VLC_FALSE;
2005 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2006 i_scale = val.i_int;
2010 rv = PushFont( &p_fonts,
2011 p_font_style->psz_fontname,
2012 p_font_style->i_font_size * i_scale / 1000,
2013 (p_font_style->i_font_color & 0xffffff) |
2014 ((p_font_style->i_font_alpha & 0xff) << 24),
2015 (p_font_style->i_karaoke_background_color & 0xffffff) |
2016 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2018 if( p_font_style->i_style_flags & STYLE_BOLD )
2020 if( p_font_style->i_style_flags & STYLE_ITALIC )
2021 b_italic = VLC_TRUE;
2022 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2027 rv = PushFont( &p_fonts,
2033 if( rv != VLC_SUCCESS )
2036 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2038 switch ( xml_ReaderNodeType( p_xml_reader ) )
2040 case XML_READER_NONE:
2042 case XML_READER_ENDELEM:
2043 psz_node = xml_ReaderName( p_xml_reader );
2047 if( !strcasecmp( "font", psz_node ) )
2048 PopFont( &p_fonts );
2049 else if( !strcasecmp( "b", psz_node ) )
2051 else if( !strcasecmp( "i", psz_node ) )
2052 b_italic = VLC_FALSE;
2053 else if( !strcasecmp( "u", psz_node ) )
2054 b_uline = VLC_FALSE;
2059 case XML_READER_STARTELEM:
2060 psz_node = xml_ReaderName( p_xml_reader );
2063 if( !strcasecmp( "font", psz_node ) )
2064 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2065 else if( !strcasecmp( "b", psz_node ) )
2067 else if( !strcasecmp( "i", psz_node ) )
2068 b_italic = VLC_TRUE;
2069 else if( !strcasecmp( "u", psz_node ) )
2071 else if( !strcasecmp( "br", psz_node ) )
2073 SetupLine( p_filter, "\n", &psz_text,
2074 pi_runs, ppi_run_lengths, ppp_styles,
2075 GetStyleFromFontStack( p_sys,
2081 else if( !strcasecmp( "k", psz_node ) )
2083 /* Only valid in karaoke */
2086 if( *pi_k_runs > 0 )
2088 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2089 *pi_k_runs, *ppi_k_run_lengths );
2091 SetupKaraoke( p_xml_reader, pi_k_runs,
2092 ppi_k_run_lengths, ppi_k_durations );
2099 case XML_READER_TEXT:
2100 psz_node = xml_ReaderValue( p_xml_reader );
2103 /* Turn any multiple-whitespaces into single spaces */
2104 char *s = strpbrk( psz_node, "\t\r\n " );
2107 int i_whitespace = strspn( s, "\t\r\n " );
2109 if( i_whitespace > 1 )
2112 strlen( s ) - i_whitespace + 1 );
2115 s = strpbrk( s, "\t\r\n " );
2117 SetupLine( p_filter, psz_node, &psz_text,
2118 pi_runs, ppi_run_lengths, ppp_styles,
2119 GetStyleFromFontStack( p_sys,
2128 if( rv != VLC_SUCCESS )
2130 psz_text = psz_text_orig;
2136 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2137 *pi_k_runs, *ppi_k_run_lengths );
2140 *pi_len = psz_text - psz_text_orig;
2142 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2147 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2151 for( k=0; k < p_sys->i_font_attachments; k++ )
2153 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2155 FT_Face p_face = NULL;
2157 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2165 vlc_bool_t match = !strcasecmp( p_face->family_name,
2166 p_style->psz_fontname );
2168 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2169 match = match && p_style->b_bold;
2171 match = match && !p_style->b_bold;
2173 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2174 match = match && p_style->b_italic;
2176 match = match && !p_style->b_italic;
2184 FT_Done_Face( p_face );
2189 return VLC_EGENERIC;
2192 static int CheckIfFontBuildComplete( filter_t *p_filter )
2194 filter_sys_t *p_sys = p_filter->p_sys;
2195 vlc_object_t *p_fb = vlc_object_find_name( p_filter->p_libvlc,
2200 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
2203 if( VLC_SUCCESS == var_Get( p_fb, "build-done", &val ))
2205 p_sys->b_fontconfig_ok = val.b_bool;
2207 if( p_sys->b_fontconfig_ok )
2210 p_sys->p_fontconfig = FcConfigGetCurrent();
2213 msg_Dbg( p_filter, "Font Build still not complete" );
2215 vlc_mutex_unlock( lock );
2216 vlc_object_release( p_fb );
2220 return VLC_EGENERIC;
2223 static int ProcessLines( filter_t *p_filter,
2228 uint32_t *pi_run_lengths,
2229 ft_style_t **pp_styles,
2230 line_desc_t **pp_lines,
2232 FT_Vector *p_result,
2234 vlc_bool_t b_karaoke,
2236 uint32_t *pi_k_run_lengths,
2237 uint32_t *pi_k_durations )
2239 filter_sys_t *p_sys = p_filter->p_sys;
2240 ft_style_t **pp_char_styles;
2241 int *p_new_positions = NULL;
2242 int8_t *p_levels = NULL;
2243 uint8_t *pi_karaoke_bar = NULL;
2247 /* Assign each character in the text string its style explicitly, so that
2248 * after the characters have been shuffled around by Fribidi, we can re-apply
2249 * the styles, and to simplify the calculation of runs within a line.
2251 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2252 if( !pp_char_styles )
2257 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2258 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2259 * we just won't be able to display the progress bar; at least we'll
2265 for( j = 0; j < i_runs; j++ )
2266 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2267 pp_char_styles[ i++ ] = pp_styles[ j ];
2269 #if defined(HAVE_FRIBIDI)
2271 ft_style_t **pp_char_styles_new;
2272 int *p_old_positions;
2273 uint32_t *p_fribidi_string;
2274 int start_pos, pos = 0;
2276 pp_char_styles_new = (ft_style_t **)
2277 malloc( i_len * sizeof( ft_style_t * ));
2279 p_fribidi_string = (uint32_t *)
2280 malloc( (i_len + 1) * sizeof(uint32_t) );
2281 p_old_positions = (int *)
2282 malloc( (i_len + 1) * sizeof( int ) );
2283 p_new_positions = (int *)
2284 malloc( (i_len + 1) * sizeof( int ) );
2285 p_levels = (int8_t *)
2286 malloc( (i_len + 1) * sizeof( int8_t ) );
2288 if( ! pp_char_styles_new ||
2289 ! p_fribidi_string ||
2290 ! p_old_positions ||
2291 ! p_new_positions ||
2294 msg_Err( p_filter, "out of memory" );
2295 if( p_levels ) free( p_levels );
2296 if( p_old_positions ) free( p_old_positions );
2297 if( p_new_positions ) free( p_new_positions );
2298 if( p_fribidi_string ) free( p_fribidi_string );
2299 if( pp_char_styles_new ) free( pp_char_styles_new );
2300 if( pi_karaoke_bar ) free( pi_karaoke_bar );
2302 free( pp_char_styles );
2306 /* Do bidi conversion line-by-line */
2309 while(pos < i_len) {
2310 if (psz_text[pos] != '\n')
2312 p_fribidi_string[pos] = psz_text[pos];
2313 pp_char_styles_new[pos] = pp_char_styles[pos];
2314 p_new_positions[pos] = pos;
2319 while(pos < i_len) {
2320 if (psz_text[pos] == '\n')
2324 if (pos > start_pos)
2326 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2327 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2328 pos - start_pos, &base_dir,
2329 (FriBidiChar*)p_fribidi_string + start_pos,
2330 p_new_positions + start_pos,
2332 p_levels + start_pos );
2333 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2335 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2336 p_old_positions[ j - start_pos ] ];
2337 p_new_positions[ j ] += start_pos;
2341 free( p_old_positions );
2342 free( pp_char_styles );
2343 pp_char_styles = pp_char_styles_new;
2344 psz_text = p_fribidi_string;
2345 p_fribidi_string[ i_len ] = 0;
2348 /* Work out the karaoke */
2349 if( pi_karaoke_bar )
2351 int64_t i_last_duration = 0;
2352 int64_t i_duration = 0;
2353 int64_t i_start_pos = 0;
2354 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2356 for( k = 0; k< i_k_runs; k++ )
2358 double fraction = 0.0;
2360 i_duration += pi_k_durations[ k ];
2362 if( i_duration < i_elapsed )
2364 /* Completely finished this run-length -
2365 * let it render normally */
2369 else if( i_elapsed < i_last_duration )
2371 /* Haven't got up to this segment yet -
2372 * render it completely in karaoke BG mode */
2378 /* Partway through this run */
2380 fraction = (double)(i_elapsed - i_last_duration) /
2381 (double)pi_k_durations[ k ];
2383 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2385 double shade = pi_k_run_lengths[ k ] * fraction;
2387 if( p_new_positions )
2388 j = p_new_positions[ i_start_pos + i ];
2390 j = i_start_pos + i;
2392 if( i < (uint32_t)shade )
2393 pi_karaoke_bar[ j ] = 0xff;
2394 else if( (double)i > shade )
2395 pi_karaoke_bar[ j ] = 0x00;
2398 shade -= (int)shade;
2399 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2400 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2404 i_last_duration = i_duration;
2405 i_start_pos += pi_k_run_lengths[ k ];
2408 if( p_levels ) free( p_levels );
2409 if( p_new_positions ) free( p_new_positions );
2411 FT_Vector tmp_result;
2413 line_desc_t *p_line = NULL;
2414 line_desc_t *p_prev = NULL;
2420 p_result->x = p_result->y = 0;
2421 tmp_result.x = tmp_result.y = 0;
2424 for( k = 0; k <= (uint32_t) i_len; k++ )
2426 if( ( k == (uint32_t) i_len ) ||
2428 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2430 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2432 /* End of the current style run */
2433 FT_Face p_face = NULL;
2436 /* Look for a match amongst our attachments first */
2437 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2439 if( !p_sys->b_fontconfig_ok )
2441 if( VLC_EGENERIC == CheckIfFontBuildComplete( p_filter ))
2442 msg_Err( p_filter, "Can't find FontBuilder thread!" );
2445 if( ! p_face && p_sys->b_fontconfig_ok )
2448 vlc_mutex_lock( &p_sys->fontconfig_lock );
2450 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2451 p_style->psz_fontname,
2455 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2457 if( psz_fontfile && ! *psz_fontfile )
2459 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2460 " so using default font", p_style->psz_fontname,
2461 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2462 (p_style->b_bold ? "(Bold)" :
2463 (p_style->b_italic ? "(Italic)" : ""))) );
2464 free( psz_fontfile );
2465 psz_fontfile = NULL;
2470 if( FT_New_Face( p_sys->p_library,
2471 psz_fontfile, i_idx, &p_face ) )
2473 free( psz_fontfile );
2474 free( pp_char_styles );
2475 #if defined(HAVE_FRIBIDI)
2478 if( pi_karaoke_bar )
2479 free( pi_karaoke_bar );
2480 return VLC_EGENERIC;
2482 free( psz_fontfile );
2486 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2488 /* We've loaded a font face which is unhelpful for actually
2489 * rendering text - fallback to the default one.
2491 FT_Done_Face( p_face );
2495 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2496 ft_encoding_unicode ) ||
2497 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2498 p_style->i_font_size ) )
2500 if( p_face ) FT_Done_Face( p_face );
2501 free( pp_char_styles );
2502 #if defined(HAVE_FRIBIDI)
2505 if( pi_karaoke_bar )
2506 free( pi_karaoke_bar );
2507 return VLC_EGENERIC;
2509 p_sys->i_use_kerning =
2510 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2513 uint32_t *psz_unicode = (uint32_t *)
2514 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2517 msg_Err( p_filter, "out of memory" );
2518 if( p_face ) FT_Done_Face( p_face );
2519 free( pp_char_styles );
2520 free( psz_unicode );
2521 #if defined(HAVE_FRIBIDI)
2524 if( pi_karaoke_bar )
2525 free( pi_karaoke_bar );
2528 memcpy( psz_unicode, psz_text + i_prev,
2529 sizeof( uint32_t ) * ( k - i_prev ) );
2530 psz_unicode[ k - i_prev ] = 0;
2531 while( *psz_unicode )
2535 if( !(p_line = NewLine( i_len - i_prev)) )
2537 msg_Err( p_filter, "out of memory" );
2538 if( p_face ) FT_Done_Face( p_face );
2539 free( pp_char_styles );
2540 free( psz_unicode );
2541 #if defined(HAVE_FRIBIDI)
2544 if( pi_karaoke_bar )
2545 free( pi_karaoke_bar );
2548 /* New Color mode only works in YUVA rendering mode --
2549 * (RGB mode has palette constraints on it). We therefore
2550 * need to populate the legacy colour fields also.
2552 p_line->b_new_color_mode = VLC_TRUE;
2553 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2554 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2555 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2556 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2557 p_line->p_next = NULL;
2559 i_pen_y += tmp_result.y;
2563 if( p_prev ) p_prev->p_next = p_line;
2564 else *pp_lines = p_line;
2567 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2568 p_style->i_font_color, p_style->b_underline,
2569 p_style->i_karaoke_bg_color,
2570 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2571 &tmp_result ) != VLC_SUCCESS )
2573 if( p_face ) FT_Done_Face( p_face );
2574 free( pp_char_styles );
2575 free( psz_unicode );
2576 #if defined(HAVE_FRIBIDI)
2579 if( pi_karaoke_bar )
2580 free( pi_karaoke_bar );
2581 return VLC_EGENERIC;
2586 p_result->x = __MAX( p_result->x, tmp_result.x );
2587 p_result->y += tmp_result.y;
2592 if( *psz_unicode == '\n')
2596 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2598 *c_ptr = *(c_ptr+1);
2603 free( psz_unicode );
2604 if( p_face ) FT_Done_Face( p_face );
2608 free( pp_char_styles );
2609 #if defined(HAVE_FRIBIDI)
2614 p_result->x = __MAX( p_result->x, tmp_result.x );
2615 p_result->y += tmp_result.y;
2618 if( pi_karaoke_bar )
2621 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2623 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2625 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2629 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2631 /* 100% BG colour will render faster if we
2632 * instead make it 100% FG colour, so leave
2633 * the ratio alone and copy the value across
2635 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2639 if( pi_karaoke_bar[ i ] & 0x80 )
2641 /* Swap Left and Right sides over for Right aligned
2642 * language text (eg. Arabic, Hebrew)
2644 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2646 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2647 p_line->p_bg_rgb[ k ] = i_tmp;
2649 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2652 /* Jump over the '\n' at the line-end */
2655 free( pi_karaoke_bar );
2661 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2662 subpicture_region_t *p_region_in )
2664 int rv = VLC_SUCCESS;
2665 stream_t *p_sub = NULL;
2666 xml_t *p_xml = NULL;
2667 xml_reader_t *p_xml_reader = NULL;
2669 if( !p_region_in || !p_region_in->psz_html )
2670 return VLC_EGENERIC;
2672 /* Reset the default fontsize in case screen metrics have changed */
2673 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2675 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2676 (uint8_t *) p_region_in->psz_html,
2677 strlen( p_region_in->psz_html ),
2681 p_xml = xml_Create( p_filter );
2684 vlc_bool_t b_karaoke = VLC_FALSE;
2686 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2689 /* Look for Root Node */
2690 if( xml_ReaderRead( p_xml_reader ) == 1 )
2692 char *psz_node = xml_ReaderName( p_xml_reader );
2694 if( !strcasecmp( "karaoke", psz_node ) )
2696 /* We're going to have to render the text a number
2697 * of times to show the progress marker on the text.
2699 var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2700 b_karaoke = VLC_TRUE;
2702 else if( !strcasecmp( "text", psz_node ) )
2704 b_karaoke = VLC_FALSE;
2708 /* Only text and karaoke tags are supported */
2709 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2710 xml_ReaderDelete( p_xml, p_xml_reader );
2711 p_xml_reader = NULL;
2723 uint32_t i_runs = 0;
2724 uint32_t i_k_runs = 0;
2725 uint32_t *pi_run_lengths = NULL;
2726 uint32_t *pi_k_run_lengths = NULL;
2727 uint32_t *pi_k_durations = NULL;
2728 ft_style_t **pp_styles = NULL;
2730 line_desc_t *p_lines = NULL;
2732 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2733 sizeof( uint32_t ) );
2738 rv = ProcessNodes( p_filter, p_xml_reader,
2739 p_region_in->p_style, psz_text, &i_len,
2740 &i_runs, &pi_run_lengths, &pp_styles,
2741 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2744 p_region_out->i_x = p_region_in->i_x;
2745 p_region_out->i_y = p_region_in->i_y;
2747 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2749 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2750 pi_run_lengths, pp_styles, &p_lines, &result,
2751 b_karaoke, i_k_runs, pi_k_run_lengths,
2755 for( k=0; k<i_runs; k++)
2756 DeleteStyle( pp_styles[k] );
2758 free( pi_run_lengths );
2761 /* Don't attempt to render text that couldn't be layed out
2764 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2766 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2768 Render( p_filter, p_region_out, p_lines,
2769 result.x, result.y );
2773 RenderYUVA( p_filter, p_region_out, p_lines,
2774 result.x, result.y );
2778 FreeLines( p_lines );
2780 xml_ReaderDelete( p_xml, p_xml_reader );
2782 xml_Delete( p_xml );
2784 stream_Delete( p_sub );
2790 static char* FontConfig_Select( FcConfig* priv, const char* family,
2791 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2794 FcPattern *pat, *p_pat;
2798 pat = FcPatternCreate();
2799 if (!pat) return NULL;
2801 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2802 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2803 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2804 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2806 FcDefaultSubstitute( pat );
2808 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2810 FcPatternDestroy( pat );
2814 p_pat = FcFontMatch( priv, pat, &result );
2815 FcPatternDestroy( pat );
2816 if( !p_pat ) return NULL;
2818 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2819 || ( val_b != FcTrue ) )
2821 FcPatternDestroy( p_pat );
2824 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2829 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2831 FcPatternDestroy( p_pat );
2836 if( strcasecmp((const char*)val_s, family ) != 0 )
2837 msg_Warn( p_filter, "fontconfig: selected font family is not"
2838 "the requested one: '%s' != '%s'\n",
2839 (const char*)val_s, family );
2842 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2844 FcPatternDestroy( p_pat );
2848 FcPatternDestroy( p_pat );
2849 return strdup( (const char*)val_s );
2853 static void FreeLine( line_desc_t *p_line )
2856 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2858 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2860 free( p_line->pp_glyphs );
2861 free( p_line->p_glyph_pos );
2862 free( p_line->p_fg_rgb );
2863 free( p_line->p_bg_rgb );
2864 free( p_line->p_fg_bg_ratio );
2865 free( p_line->pi_underline_offset );
2866 free( p_line->pi_underline_thickness );
2870 static void FreeLines( line_desc_t *p_lines )
2872 line_desc_t *p_line, *p_next;
2874 if( !p_lines ) return;
2876 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2878 p_next = p_line->p_next;
2883 static line_desc_t *NewLine( int i_count )
2885 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2887 if( !p_line ) return NULL;
2888 p_line->i_height = 0;
2889 p_line->i_width = 0;
2890 p_line->p_next = NULL;
2892 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2893 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2894 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2895 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2896 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2897 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2898 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2899 if( ( p_line->pp_glyphs == NULL ) ||
2900 ( p_line->p_glyph_pos == NULL ) ||
2901 ( p_line->p_fg_rgb == NULL ) ||
2902 ( p_line->p_bg_rgb == NULL ) ||
2903 ( p_line->p_fg_bg_ratio == NULL ) ||
2904 ( p_line->pi_underline_offset == NULL ) ||
2905 ( p_line->pi_underline_thickness == NULL ) )
2907 if( p_line->pi_underline_thickness )
2908 free( p_line->pi_underline_thickness );
2909 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2910 if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2911 if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2912 if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2913 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2914 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2918 p_line->pp_glyphs[0] = NULL;
2919 p_line->b_new_color_mode = VLC_FALSE;
2924 static int GetFontSize( filter_t *p_filter )
2926 filter_sys_t *p_sys = p_filter->p_sys;
2930 if( p_sys->i_default_font_size )
2932 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2933 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2935 i_size = p_sys->i_default_font_size;
2939 var_Get( p_filter, "freetype-rel-fontsize", &val );
2940 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2941 p_filter->p_sys->i_display_height =
2942 p_filter->fmt_out.video.i_height;
2946 msg_Warn( p_filter, "invalid fontsize, using 12" );
2947 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2948 i_size = 12 * val.i_int / 1000;
2955 static int SetFontSize( filter_t *p_filter, int i_size )
2957 filter_sys_t *p_sys = p_filter->p_sys;
2961 i_size = GetFontSize( p_filter );
2963 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2966 p_sys->i_font_size = i_size;
2968 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2970 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2971 return VLC_EGENERIC;
2977 static void YUVFromRGB( uint32_t i_argb,
2978 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2980 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2981 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2982 int i_blue = ( i_argb & 0x000000ff );
2984 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2985 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2986 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2987 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2988 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2989 -585 * i_blue + 4096 + 1048576) >> 13, 240);