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 * );
95 static line_desc_t *NewLine( int );
97 static int GetFontSize( filter_t *p_filter );
98 static int SetFontSize( filter_t *, int );
99 static void YUVFromRGB( uint32_t i_argb,
100 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
102 /*****************************************************************************
104 *****************************************************************************/
105 #define FONT_TEXT N_("Font")
106 #define FONT_LONGTEXT N_("Filename for the font you want to use")
107 #define FONTSIZE_TEXT N_("Font size in pixels")
108 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
109 "that will be rendered on the video. " \
110 "If set to something different than 0 this option will override the " \
111 "relative font size." )
112 #define OPACITY_TEXT N_("Opacity")
113 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
114 "text that will be rendered on the video. 0 = transparent, " \
115 "255 = totally opaque. " )
116 #define COLOR_TEXT N_("Text default color")
117 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
118 "the video. This must be an hexadecimal (like HTML colors). The first two "\
119 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
120 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
121 #define FONTSIZER_TEXT N_("Relative font size")
122 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
123 "fonts that will be rendered on the video. If absolute font size is set, "\
124 "relative size will be overriden." )
126 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
127 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
128 N_("Large"), N_("Larger") };
129 #define YUVP_TEXT N_("Use YUVP renderer")
130 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
131 "This option is only needed if you want to encode into DVB subtitles" )
132 #define EFFECT_TEXT N_("Font Effect")
133 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
134 "text to improve its readability." )
136 #define EFFECT_BACKGROUND 1
137 #define EFFECT_OUTLINE 2
138 #define EFFECT_OUTLINE_FAT 3
140 static int pi_effects[] = { 1, 2, 3 };
141 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
143 static int pi_color_values[] = {
144 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
145 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
146 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
148 static const char *ppsz_color_descriptions[] = {
149 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
150 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
151 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
154 set_shortname( _("Text renderer"));
155 set_description( _("Freetype2 font renderer") );
156 set_category( CAT_VIDEO );
157 set_subcategory( SUBCAT_VIDEO_SUBPIC );
159 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
162 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
163 FONTSIZE_LONGTEXT, VLC_TRUE );
165 /* opacity valid on 0..255, with default 255 = fully opaque */
166 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
167 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
169 /* hook to the color values list, with default 0x00ffffff = white */
170 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
171 COLOR_LONGTEXT, VLC_FALSE );
172 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
174 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
175 FONTSIZER_LONGTEXT, VLC_FALSE );
176 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
177 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
178 EFFECT_LONGTEXT, VLC_FALSE );
179 change_integer_list( pi_effects, ppsz_effects_text, 0 );
181 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
182 YUVP_LONGTEXT, VLC_TRUE );
183 set_capability( "text renderer", 100 );
184 add_shortcut( "text" );
185 set_callbacks( Create, Destroy );
190 /** NULL-terminated list of glyphs making the string */
191 FT_BitmapGlyph *pp_glyphs;
192 /** list of relative positions for the glyphs */
193 FT_Vector *p_glyph_pos;
194 /** list of RGB information for styled text
195 * -- if the rendering mode supports it (RenderYUVA) and
196 * b_new_color_mode is set, then it becomes possible to
197 * have multicoloured text within the subtitles. */
200 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
201 vlc_bool_t b_new_color_mode;
202 /** underline information -- only supplied if text should be underlined */
203 uint16_t *pi_underline_offset;
204 uint16_t *pi_underline_thickness;
208 int i_red, i_green, i_blue;
214 typedef struct font_stack_t font_stack_t;
219 uint32_t i_color; /* ARGB */
220 uint32_t i_karaoke_bg_color; /* ARGB */
222 font_stack_t *p_next;
228 uint32_t i_font_color; /* ARGB */
229 uint32_t i_karaoke_bg_color; /* ARGB */
232 vlc_bool_t b_underline;
236 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
237 static void FreeLines( line_desc_t * );
238 static void FreeLine( line_desc_t * );
239 #ifdef HAVE_FONTCONFIG
240 static void FontBuilder( vlc_object_t *p_this);
243 /*****************************************************************************
244 * filter_sys_t: freetype local data
245 *****************************************************************************
246 * This structure is part of the video output thread descriptor.
247 * It describes the freetype specific properties of an output thread.
248 *****************************************************************************/
251 FT_Library p_library; /* handle to library */
252 FT_Face p_face; /* handle to face object */
253 vlc_bool_t i_use_kerning;
254 uint8_t i_font_opacity;
259 int i_default_font_size;
260 int i_display_height;
261 #ifdef HAVE_FONTCONFIG
262 FcConfig *p_fontconfig;
263 vlc_bool_t b_fontconfig_ok;
264 vlc_mutex_t fontconfig_lock;
267 input_attachment_t **pp_font_attachments;
268 int i_font_attachments;
271 /*****************************************************************************
272 * Create: allocates osd-text video thread output method
273 *****************************************************************************
274 * This function allocates and initializes a Clone vout method.
275 *****************************************************************************/
276 static int Create( vlc_object_t *p_this )
278 filter_t *p_filter = (filter_t *)p_this;
280 char *psz_fontfile = NULL;
284 /* Allocate structure */
285 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
288 msg_Err( p_filter, "out of memory" );
292 p_sys->p_library = 0;
293 p_sys->i_font_size = 0;
294 p_sys->i_display_height = 0;
296 var_Create( p_filter, "freetype-font",
297 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
298 var_Create( p_filter, "freetype-fontsize",
299 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
300 var_Create( p_filter, "freetype-rel-fontsize",
301 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
302 var_Create( p_filter, "freetype-opacity",
303 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
304 var_Create( p_filter, "freetype-effect",
305 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
306 var_Get( p_filter, "freetype-opacity", &val );
307 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
308 var_Create( p_filter, "freetype-color",
309 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
310 var_Get( p_filter, "freetype-color", &val );
311 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
312 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
314 /* Look what method was requested */
315 var_Get( p_filter, "freetype-font", &val );
316 psz_fontfile = val.psz_string;
317 if( !psz_fontfile || !*psz_fontfile )
319 if( psz_fontfile ) free( psz_fontfile );
320 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
323 msg_Err( p_filter, "out of memory" );
327 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
328 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
329 #elif defined(__APPLE__)
330 strcpy( psz_fontfile, DEFAULT_FONT );
332 msg_Err( p_filter, "user didn't specify a font" );
337 #ifdef HAVE_FONTCONFIG
338 vlc_mutex_init( p_filter, &p_sys->fontconfig_lock );
339 p_sys->b_fontconfig_ok = VLC_FALSE;
341 p_sys->p_fontconfig = FcInitLoadConfig();
343 if( p_sys->p_fontconfig )
345 /* Normally this doesn't take very long, but an initial build of
346 * the fontconfig database or the addition of a lot of new fonts
347 * can cause it to take several minutes for a large number of fonts.
348 * Even a small number can take several seconds - much longer than
349 * we can afford to block, so we build the list in the background
350 * and if it succeeds we allow fontconfig to be used.
352 if( vlc_thread_create( p_filter, "fontlist builder", FontBuilder,
353 VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
355 /* Don't destroy the fontconfig object - we won't be able to do
356 * italics or bold or change the font face, but we will still
357 * be able to do underline and change the font size.
359 msg_Warn( p_filter, "fontconfig database builder thread can't "
360 "be launched. Font styling support will be limited." );
365 msg_Warn( p_filter, "Couldn't initialise Fontconfig. "
366 "Font styling won't be available." );
369 i_error = FT_Init_FreeType( &p_sys->p_library );
372 msg_Err( p_filter, "couldn't initialize freetype" );
375 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
377 if( i_error == FT_Err_Unknown_File_Format )
379 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
384 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
388 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
391 msg_Err( p_filter, "font has no unicode translation table" );
395 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
397 var_Get( p_filter, "freetype-fontsize", &val );
398 p_sys->i_default_font_size = val.i_int;
399 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
401 if( psz_fontfile ) free( psz_fontfile );
403 p_sys->pp_font_attachments = NULL;
404 p_sys->i_font_attachments = 0;
406 p_filter->pf_render_text = RenderText;
407 #ifdef HAVE_FONTCONFIG
408 if( p_sys->p_fontconfig )
409 p_filter->pf_render_html = RenderHtml;
412 p_filter->pf_render_html = NULL;
414 LoadFontsFromAttachments( p_filter );
419 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
420 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
421 if( psz_fontfile ) free( psz_fontfile );
426 /*****************************************************************************
427 * Destroy: destroy Clone video thread output method
428 *****************************************************************************
429 * Clean up all data and library connections
430 *****************************************************************************/
431 static void Destroy( vlc_object_t *p_this )
433 filter_t *p_filter = (filter_t *)p_this;
434 filter_sys_t *p_sys = p_filter->p_sys;
436 if( p_sys->pp_font_attachments )
440 for( k = 0; k < p_sys->i_font_attachments; k++ )
442 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
445 free( p_sys->pp_font_attachments );
448 #ifdef HAVE_FONTCONFIG
449 vlc_mutex_destroy( &p_sys->fontconfig_lock );
451 if( p_sys->p_fontconfig )
453 FcConfigDestroy( p_sys->p_fontconfig );
454 p_sys->p_fontconfig = NULL;
456 /* FcFini asserts calling the subfunction FcCacheFini()
457 * even if no other library functions have been made since FcInit(),
458 * so don't call it. */
460 FT_Done_Face( p_sys->p_face );
461 FT_Done_FreeType( p_sys->p_library );
465 #ifdef HAVE_FONTCONFIG
467 static void FontBuilder( vlc_object_t *p_this)
469 filter_t *p_filter = (filter_t*)p_this;
470 filter_sys_t *p_sys = p_filter->p_sys;
473 /* Find the session to announce */
474 vlc_mutex_lock( &p_sys->fontconfig_lock );
476 msg_Dbg( p_filter, "Building font database..." );
478 if(! FcConfigBuildFonts( p_sys->p_fontconfig ))
480 /* Don't destroy the fontconfig object - we won't be able to do
481 * italics or bold or change the font face, but we will still
482 * be able to do underline and change the font size.
484 msg_Err( p_filter, "fontconfig database can't be built. "
485 "Font styling won't be available" );
489 msg_Dbg( p_filter, "Finished building font database." );
490 if( t1 > 0 && t2 > 0 )
491 msg_Dbg( p_filter, "Took %ld seconds", t2 - t1 );
493 FcConfigSetCurrent( p_sys->p_fontconfig );
494 p_sys->b_fontconfig_ok = VLC_TRUE;
496 vlc_mutex_unlock( &p_sys->fontconfig_lock );
501 /*****************************************************************************
502 * Make any TTF/OTF fonts present in the attachments of the media file
503 * and store them for later use by the FreeType Engine
504 *****************************************************************************/
505 static int LoadFontsFromAttachments( filter_t *p_filter )
507 filter_sys_t *p_sys = p_filter->p_sys;
508 input_thread_t *p_input;
509 input_attachment_t **pp_attachments;
510 int i_attachments_cnt;
512 int rv = VLC_SUCCESS;
514 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
518 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
520 vlc_object_release(p_input);
524 p_sys->i_font_attachments = 0;
525 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
526 if(! p_sys->pp_font_attachments )
529 for( k = 0; k < i_attachments_cnt; k++ )
531 input_attachment_t *p_attach = pp_attachments[k];
533 if( p_sys->pp_font_attachments )
535 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
536 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
537 ( p_attach->i_data > 0 ) &&
538 ( p_attach->p_data != NULL ) )
540 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
544 vlc_input_attachment_Delete( p_attach );
549 vlc_input_attachment_Delete( p_attach );
552 free( pp_attachments );
554 vlc_object_release(p_input);
559 /*****************************************************************************
560 * Render: place string in picture
561 *****************************************************************************
562 * This function merges the previously rendered freetype glyphs into a picture
563 *****************************************************************************/
564 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
565 line_desc_t *p_line, int i_width, int i_height )
567 static uint8_t pi_gamma[16] =
568 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
569 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
573 int i, x, y, i_pitch;
574 uint8_t i_y; /* YUV values, derived from incoming RGB */
576 subpicture_region_t *p_region_tmp;
578 /* Create a new subpicture region */
579 memset( &fmt, 0, sizeof(video_format_t) );
580 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
582 fmt.i_width = fmt.i_visible_width = i_width + 4;
583 fmt.i_height = fmt.i_visible_height = i_height + 4;
584 if( p_region->fmt.i_visible_width > 0 )
585 fmt.i_visible_width = p_region->fmt.i_visible_width;
586 if( p_region->fmt.i_visible_height > 0 )
587 fmt.i_visible_height = p_region->fmt.i_visible_height;
588 fmt.i_x_offset = fmt.i_y_offset = 0;
589 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
592 msg_Err( p_filter, "cannot allocate SPU region" );
596 p_region->fmt = p_region_tmp->fmt;
597 p_region->picture = p_region_tmp->picture;
598 free( p_region_tmp );
600 /* Calculate text color components */
601 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
602 25 * p_line->i_blue + 128) >> 8) + 16;
603 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
604 112 * p_line->i_blue + 128) >> 8) + 128;
605 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
606 18 * p_line->i_blue + 128) >> 8) + 128;
609 fmt.p_palette->i_entries = 16;
610 for( i = 0; i < 8; i++ )
612 fmt.p_palette->palette[i][0] = 0;
613 fmt.p_palette->palette[i][1] = 0x80;
614 fmt.p_palette->palette[i][2] = 0x80;
615 fmt.p_palette->palette[i][3] = pi_gamma[i];
616 fmt.p_palette->palette[i][3] =
617 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
619 for( i = 8; i < fmt.p_palette->i_entries; i++ )
621 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
622 fmt.p_palette->palette[i][1] = i_u;
623 fmt.p_palette->palette[i][2] = i_v;
624 fmt.p_palette->palette[i][3] = pi_gamma[i];
625 fmt.p_palette->palette[i][3] =
626 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
629 p_dst = p_region->picture.Y_PIXELS;
630 i_pitch = p_region->picture.Y_PITCH;
632 /* Initialize the region pixels */
633 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
635 for( ; p_line != NULL; p_line = p_line->p_next )
637 int i_glyph_tmax = 0;
638 int i_bitmap_offset, i_offset, i_align_offset = 0;
639 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
641 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
642 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
645 if( p_line->i_width < i_width )
647 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
649 i_align_offset = i_width - p_line->i_width;
651 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
653 i_align_offset = ( i_width - p_line->i_width ) / 2;
657 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
659 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
661 i_offset = ( p_line->p_glyph_pos[ i ].y +
662 i_glyph_tmax - p_glyph->top + 2 ) *
663 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
666 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
668 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
670 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
672 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
679 /* Outlining (find something better than nearest neighbour filtering ?) */
682 uint8_t *p_dst = p_region->picture.Y_PIXELS;
683 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
684 uint8_t left, current;
686 for( y = 1; y < (int)fmt.i_height - 1; y++ )
688 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
689 p_dst += p_region->picture.Y_PITCH;
692 for( x = 1; x < (int)fmt.i_width - 1; x++ )
695 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
696 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;
700 memset( p_top, 0, fmt.i_width );
706 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
707 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
708 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
709 int i_glyph_tmax, int i_align_offset,
710 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
711 subpicture_region_t *p_region)
715 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
717 p_dst_y = p_region->picture.Y_PIXELS;
718 p_dst_u = p_region->picture.U_PIXELS;
719 p_dst_v = p_region->picture.V_PIXELS;
720 p_dst_a = p_region->picture.A_PIXELS;
721 i_pitch = p_region->picture.A_PITCH;
723 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
724 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
726 for( y = 0; y < i_line_thickness; y++ )
728 int i_extra = p_this_glyph->bitmap.width;
732 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
733 (p_this_glyph_pos->x + p_this_glyph->left);
735 for( x = 0; x < i_extra; x++ )
737 vlc_bool_t b_ok = VLC_TRUE;
739 /* break the underline around the tails of any glyphs which cross it */
740 for( z = x - i_line_thickness;
741 z < x + i_line_thickness && b_ok;
744 if( p_next_glyph && ( z >= i_extra ) )
746 int i_row = i_line_offset + p_next_glyph->top + y;
748 if( ( p_next_glyph->bitmap.rows > i_row ) &&
749 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
754 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
756 int i_row = i_line_offset + p_this_glyph->top + y;
758 if( ( p_this_glyph->bitmap.rows > i_row ) &&
759 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
768 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
769 p_dst_u[i_offset+x] = i_u;
770 p_dst_v[i_offset+x] = i_v;
771 p_dst_a[i_offset+x] = 255;
778 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
780 uint8_t *p_dst = p_region->picture.A_PIXELS;
781 int i_pitch = p_region->picture.A_PITCH;
784 for( ; p_line != NULL; p_line = p_line->p_next )
786 int i_glyph_tmax=0, i = 0;
787 int i_bitmap_offset, i_offset, i_align_offset = 0;
788 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
790 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
791 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
794 if( p_line->i_width < i_width )
796 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
798 i_align_offset = i_width - p_line->i_width;
800 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
802 i_align_offset = ( i_width - p_line->i_width ) / 2;
806 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
808 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
810 i_offset = ( p_line->p_glyph_pos[ i ].y +
811 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
812 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
813 i_align_offset +xoffset;
815 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
817 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
819 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
820 if( p_dst[i_offset+x] <
821 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
823 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
832 /*****************************************************************************
833 * Render: place string in picture
834 *****************************************************************************
835 * This function merges the previously rendered freetype glyphs into a picture
836 *****************************************************************************/
837 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
838 line_desc_t *p_line, int i_width, int i_height )
840 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
842 int i, x, y, i_pitch, i_alpha;
843 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
844 subpicture_region_t *p_region_tmp;
846 if( i_width == 0 || i_height == 0 )
849 /* Create a new subpicture region */
850 memset( &fmt, 0, sizeof(video_format_t) );
851 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
853 fmt.i_width = fmt.i_visible_width = i_width + 6;
854 fmt.i_height = fmt.i_visible_height = i_height + 6;
855 if( p_region->fmt.i_visible_width > 0 )
856 fmt.i_visible_width = p_region->fmt.i_visible_width;
857 if( p_region->fmt.i_visible_height > 0 )
858 fmt.i_visible_height = p_region->fmt.i_visible_height;
859 fmt.i_x_offset = fmt.i_y_offset = 0;
860 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
863 msg_Err( p_filter, "cannot allocate SPU region" );
867 p_region->fmt = p_region_tmp->fmt;
868 p_region->picture = p_region_tmp->picture;
869 free( p_region_tmp );
871 /* Calculate text color components */
872 YUVFromRGB( (p_line->i_red << 16) |
873 (p_line->i_green << 8) |
876 i_alpha = p_line->i_alpha;
878 p_dst_y = p_region->picture.Y_PIXELS;
879 p_dst_u = p_region->picture.U_PIXELS;
880 p_dst_v = p_region->picture.V_PIXELS;
881 p_dst_a = p_region->picture.A_PIXELS;
882 i_pitch = p_region->picture.A_PITCH;
884 /* Initialize the region pixels */
885 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
887 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
888 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
889 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
890 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
894 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
895 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
896 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
897 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
899 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
900 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
902 DrawBlack( p_line, i_width, p_region, 0, 0);
903 DrawBlack( p_line, i_width, p_region, -1, 0);
904 DrawBlack( p_line, i_width, p_region, 0, -1);
905 DrawBlack( p_line, i_width, p_region, 1, 0);
906 DrawBlack( p_line, i_width, p_region, 0, 1);
909 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
911 DrawBlack( p_line, i_width, p_region, -1, -1);
912 DrawBlack( p_line, i_width, p_region, -1, 1);
913 DrawBlack( p_line, i_width, p_region, 1, -1);
914 DrawBlack( p_line, i_width, p_region, 1, 1);
916 DrawBlack( p_line, i_width, p_region, -2, 0);
917 DrawBlack( p_line, i_width, p_region, 0, -2);
918 DrawBlack( p_line, i_width, p_region, 2, 0);
919 DrawBlack( p_line, i_width, p_region, 0, 2);
921 DrawBlack( p_line, i_width, p_region, -2, -2);
922 DrawBlack( p_line, i_width, p_region, -2, 2);
923 DrawBlack( p_line, i_width, p_region, 2, -2);
924 DrawBlack( p_line, i_width, p_region, 2, 2);
926 DrawBlack( p_line, i_width, p_region, -3, 0);
927 DrawBlack( p_line, i_width, p_region, 0, -3);
928 DrawBlack( p_line, i_width, p_region, 3, 0);
929 DrawBlack( p_line, i_width, p_region, 0, 3);
932 for( ; p_line != NULL; p_line = p_line->p_next )
934 int i_glyph_tmax = 0;
935 int i_bitmap_offset, i_offset, i_align_offset = 0;
936 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
938 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
939 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
942 if( p_line->i_width < i_width )
944 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
946 i_align_offset = i_width - p_line->i_width;
948 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
950 i_align_offset = ( i_width - p_line->i_width ) / 2;
954 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
956 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
958 i_offset = ( p_line->p_glyph_pos[ i ].y +
959 i_glyph_tmax - p_glyph->top + 3 ) *
960 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
963 if( p_line->b_new_color_mode )
965 /* Every glyph can (and in fact must) have its own color */
966 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
969 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
971 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
973 uint8_t i_y_local = i_y;
974 uint8_t i_u_local = i_u;
975 uint8_t i_v_local = i_v;
977 if( p_line->p_fg_bg_ratio != 0x00 )
979 int i_split = p_glyph->bitmap.width *
980 p_line->p_fg_bg_ratio[ i ] / 0x7f;
984 YUVFromRGB( p_line->p_bg_rgb[ i ],
985 &i_y_local, &i_u_local, &i_v_local );
989 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
991 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
992 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
994 p_dst_u[i_offset+x] = i_u;
995 p_dst_v[i_offset+x] = i_v;
997 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
998 p_dst_a[i_offset+x] = 0xff;
1001 i_offset += i_pitch;
1004 if( p_line->pi_underline_thickness[ i ] )
1006 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1007 p_line->pi_underline_offset[ i ],
1008 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1009 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1010 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1011 i_glyph_tmax, i_align_offset,
1012 i_y, i_u, i_v, i_alpha,
1018 /* Apply the alpha setting */
1019 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1020 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1026 * This function renders a text subpicture region into another one.
1027 * It also calculates the size needed for this string, and renders the
1028 * needed glyphs into memory. It is used as pf_add_string callback in
1029 * the vout method by this module
1031 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1032 subpicture_region_t *p_region_in )
1034 filter_sys_t *p_sys = p_filter->p_sys;
1035 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1036 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1037 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1038 int i_string_length;
1040 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1041 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1051 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1052 psz_string = p_region_in->psz_text;
1053 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1055 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1056 i_scale = val.i_int;
1058 if( p_region_in->p_style )
1060 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1061 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1062 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1066 i_font_color = p_sys->i_font_color;
1067 i_font_alpha = 255 - p_sys->i_font_opacity;
1068 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1071 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1072 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1073 SetFontSize( p_filter, i_font_size );
1075 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1076 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1077 i_blue = i_font_color & 0x000000FF;
1079 result.x = result.y = 0;
1080 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1082 psz_unicode = psz_unicode_orig =
1083 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1084 if( psz_unicode == NULL )
1086 msg_Err( p_filter, "out of memory" );
1089 #if defined(WORDS_BIGENDIAN)
1090 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1092 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1094 if( iconv_handle == (vlc_iconv_t)-1 )
1096 msg_Warn( p_filter, "unable to do conversion" );
1102 const char *p_in_buffer = psz_string;
1103 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1104 i_in_bytes = strlen( psz_string );
1105 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1106 i_out_bytes_left = i_out_bytes;
1107 p_out_buffer = (char *)psz_unicode;
1108 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1109 &p_out_buffer, &i_out_bytes_left );
1111 vlc_iconv_close( iconv_handle );
1115 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1116 "bytes left %u", (unsigned)i_in_bytes );
1119 *(uint32_t*)p_out_buffer = 0;
1120 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1123 #if defined(HAVE_FRIBIDI)
1125 uint32_t *p_fribidi_string;
1126 int start_pos, pos = 0;
1128 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1129 if( !p_fribidi_string )
1131 msg_Err( p_filter, "out of memory" );
1135 /* Do bidi conversion line-by-line */
1136 while(pos < i_string_length)
1138 while(pos < i_string_length) {
1139 i_char = psz_unicode[pos];
1140 if (i_char != '\r' && i_char != '\n')
1142 p_fribidi_string[pos] = i_char;
1146 while(pos < i_string_length) {
1147 i_char = psz_unicode[pos];
1148 if (i_char == '\r' || i_char == '\n')
1152 if (pos > start_pos)
1154 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1155 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
1156 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
1160 free( psz_unicode_orig );
1161 psz_unicode = psz_unicode_orig = p_fribidi_string;
1162 p_fribidi_string[ i_string_length ] = 0;
1166 /* Calculate relative glyph positions and a bounding box for the
1168 if( !(p_line = NewLine( strlen( psz_string ))) )
1170 msg_Err( p_filter, "out of memory" );
1174 i_pen_x = i_pen_y = 0;
1176 psz_line_start = psz_unicode;
1178 #define face p_sys->p_face
1179 #define glyph face->glyph
1181 while( *psz_unicode )
1183 i_char = *psz_unicode++;
1184 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1189 if( i_char == '\n' )
1191 psz_line_start = psz_unicode;
1192 if( !(p_next = NewLine( strlen( psz_string ))) )
1194 msg_Err( p_filter, "out of memory" );
1197 p_line->p_next = p_next;
1198 p_line->i_width = line.xMax;
1199 p_line->i_height = face->size->metrics.height >> 6;
1200 p_line->pp_glyphs[ i ] = NULL;
1201 p_line->i_alpha = i_font_alpha;
1202 p_line->i_red = i_red;
1203 p_line->i_green = i_green;
1204 p_line->i_blue = i_blue;
1207 result.x = __MAX( result.x, line.xMax );
1208 result.y += face->size->metrics.height >> 6;
1211 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1212 i_pen_y += face->size->metrics.height >> 6;
1214 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1219 i_glyph_index = FT_Get_Char_Index( face, i_char );
1220 if( p_sys->i_use_kerning && i_glyph_index
1224 FT_Get_Kerning( face, i_previous, i_glyph_index,
1225 ft_kerning_default, &delta );
1226 i_pen_x += delta.x >> 6;
1229 p_line->p_glyph_pos[ i ].x = i_pen_x;
1230 p_line->p_glyph_pos[ i ].y = i_pen_y;
1231 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1234 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1238 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1241 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1245 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1246 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1249 FT_Done_Glyph( tmp_glyph );
1252 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1255 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1256 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1257 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1259 p_line->pp_glyphs[ i ] = NULL;
1261 p_line = NewLine( strlen( psz_string ));
1262 if( p_prev ) p_prev->p_next = p_line;
1263 else p_lines = p_line;
1265 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1269 if( psz_unicode == psz_line_start )
1271 msg_Warn( p_filter, "unbreakable string" );
1276 *psz_unicode = '\n';
1278 psz_unicode = psz_line_start;
1281 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1284 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1285 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1287 i_previous = i_glyph_index;
1288 i_pen_x += glyph->advance.x >> 6;
1292 p_line->i_width = line.xMax;
1293 p_line->i_height = face->size->metrics.height >> 6;
1294 p_line->pp_glyphs[ i ] = NULL;
1295 p_line->i_alpha = i_font_alpha;
1296 p_line->i_red = i_red;
1297 p_line->i_green = i_green;
1298 p_line->i_blue = i_blue;
1299 result.x = __MAX( result.x, line.xMax );
1300 result.y += line.yMax - line.yMin;
1305 p_region_out->i_x = p_region_in->i_x;
1306 p_region_out->i_y = p_region_in->i_y;
1308 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1309 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1311 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1313 if( psz_unicode_orig ) free( psz_unicode_orig );
1314 FreeLines( p_lines );
1318 if( psz_unicode_orig ) free( psz_unicode_orig );
1319 FreeLines( p_lines );
1320 return VLC_EGENERIC;
1323 #ifdef HAVE_FONTCONFIG
1324 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1325 uint32_t i_font_color, uint32_t i_karaoke_bg_color, vlc_bool_t b_bold,
1326 vlc_bool_t b_italic, vlc_bool_t b_uline )
1328 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1332 p_style->i_font_size = i_font_size;
1333 p_style->i_font_color = i_font_color;
1334 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1335 p_style->b_italic = b_italic;
1336 p_style->b_bold = b_bold;
1337 p_style->b_underline = b_uline;
1339 p_style->psz_fontname = strdup( psz_fontname );
1344 static void DeleteStyle( ft_style_t *p_style )
1348 if( p_style->psz_fontname )
1349 free( p_style->psz_fontname );
1354 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1361 if(( s1->i_font_size == s2->i_font_size ) &&
1362 ( s1->i_font_color == s2->i_font_color ) &&
1363 ( s1->b_italic == s2->b_italic ) &&
1364 ( s1->b_bold == s2->b_bold ) &&
1365 ( s1->b_underline == s2->b_underline ) &&
1366 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1373 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1374 uint32_t i_color, uint32_t i_karaoke_bg_color )
1376 font_stack_t *p_new;
1379 return VLC_EGENERIC;
1381 p_new = malloc( sizeof( font_stack_t ) );
1385 p_new->p_next = NULL;
1388 p_new->psz_name = strdup( psz_name );
1390 p_new->psz_name = NULL;
1392 p_new->i_size = i_size;
1393 p_new->i_color = i_color;
1394 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1402 font_stack_t *p_last;
1404 for( p_last = *p_font;
1406 p_last = p_last->p_next )
1409 p_last->p_next = p_new;
1414 static int PopFont( font_stack_t **p_font )
1416 font_stack_t *p_last, *p_next_to_last;
1418 if( !p_font || !*p_font )
1419 return VLC_EGENERIC;
1421 p_next_to_last = NULL;
1422 for( p_last = *p_font;
1424 p_last = p_last->p_next )
1426 p_next_to_last = p_last;
1429 if( p_next_to_last )
1430 p_next_to_last->p_next = NULL;
1434 free( p_last->psz_name );
1440 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1441 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1443 font_stack_t *p_last;
1445 if( !p_font || !*p_font )
1446 return VLC_EGENERIC;
1448 for( p_last=*p_font;
1450 p_last=p_last->p_next )
1453 *psz_name = p_last->psz_name;
1454 *i_size = p_last->i_size;
1455 *i_color = p_last->i_color;
1456 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1461 static void IconvText( filter_t *p_filter, const char *psz_string,
1462 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1464 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1466 /* If memory hasn't been allocated for our output string, allocate it here
1467 * - the calling function must now be responsible for freeing it.
1469 if( !*ppsz_unicode )
1470 *ppsz_unicode = (uint32_t *)
1471 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1473 /* We don't need to handle a NULL pointer in *ppsz_unicode
1474 * if we are instead testing for a non NULL value like we are here */
1478 #if defined(WORDS_BIGENDIAN)
1479 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1481 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1483 if( iconv_handle != (vlc_iconv_t)-1 )
1485 char *p_in_buffer, *p_out_buffer;
1486 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1487 i_in_bytes = strlen( psz_string );
1488 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1489 i_out_bytes_left = i_out_bytes;
1490 p_in_buffer = (char *) psz_string;
1491 p_out_buffer = (char *) *ppsz_unicode;
1492 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1493 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1495 vlc_iconv_close( iconv_handle );
1499 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1500 "bytes left %u", (unsigned)i_in_bytes );
1504 *(uint32_t*)p_out_buffer = 0;
1506 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1511 msg_Warn( p_filter, "unable to do conversion" );
1516 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1517 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1518 vlc_bool_t b_uline )
1520 ft_style_t *p_style = NULL;
1522 char *psz_fontname = NULL;
1523 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1524 uint32_t i_karaoke_bg_color = i_font_color;
1525 int i_font_size = p_sys->i_font_size;
1527 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1528 &i_font_color, &i_karaoke_bg_color ))
1530 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1531 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1536 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1537 vlc_bool_t b_uline, int i_karaoke_bgcolor,
1538 line_desc_t *p_line, uint32_t *psz_unicode,
1539 int *pi_pen_x, int i_pen_y, int *pi_start,
1540 FT_Vector *p_result )
1545 vlc_bool_t b_first_on_line = VLC_TRUE;
1548 int i_pen_x_start = *pi_pen_x;
1550 uint32_t *psz_unicode_start = psz_unicode;
1552 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1554 /* Account for part of line already in position */
1555 for( i=0; i<*pi_start; i++ )
1559 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1560 ft_glyph_bbox_pixels, &glyph_size );
1562 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1563 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1564 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1565 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1571 b_first_on_line = VLC_FALSE;
1573 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1579 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1580 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1584 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1585 ft_kerning_default, &delta );
1586 *pi_pen_x += delta.x >> 6;
1588 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1589 p_line->p_glyph_pos[ i ].y = i_pen_y;
1591 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1595 "unable to render text FT_Load_Glyph returned %d", i_error );
1596 p_line->pp_glyphs[ i ] = NULL;
1597 return VLC_EGENERIC;
1599 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1603 "unable to render text FT_Get_Glyph returned %d", i_error );
1604 p_line->pp_glyphs[ i ] = NULL;
1605 return VLC_EGENERIC;
1607 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1608 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1611 FT_Done_Glyph( tmp_glyph );
1616 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1617 p_face->size->metrics.y_scale));
1618 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1619 p_face->size->metrics.y_scale));
1621 p_line->pi_underline_offset[ i ] =
1622 ( aOffset < 0 ) ? -aOffset : aOffset;
1623 p_line->pi_underline_thickness[ i ] =
1624 ( aSize < 0 ) ? -aSize : aSize;
1626 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1627 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1628 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1629 p_line->p_fg_bg_ratio[ i ] = 0x00;
1631 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1632 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1633 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1635 while( --i > *pi_start )
1637 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1640 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1644 if( psz_unicode == psz_unicode_start )
1646 if( b_first_on_line )
1648 msg_Warn( p_filter, "unbreakable string" );
1649 p_line->pp_glyphs[ i ] = NULL;
1650 return VLC_EGENERIC;
1652 *pi_pen_x = i_pen_x_start;
1654 p_line->i_width = line.xMax;
1655 p_line->i_height = __MAX( p_line->i_height,
1656 p_face->size->metrics.height >> 6 );
1657 p_line->pp_glyphs[ i ] = NULL;
1659 p_result->x = __MAX( p_result->x, line.xMax );
1660 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1661 i_yMax - i_yMin ) );
1668 *psz_unicode = '\n';
1670 psz_unicode = psz_unicode_start;
1671 *pi_pen_x = i_pen_x_start;
1679 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1680 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1682 i_previous = i_glyph_index;
1683 *pi_pen_x += p_face->glyph->advance.x >> 6;
1686 p_line->i_width = line.xMax;
1687 p_line->i_height = __MAX( p_line->i_height,
1688 p_face->size->metrics.height >> 6 );
1689 p_line->pp_glyphs[ i ] = NULL;
1691 p_result->x = __MAX( p_result->x, line.xMax );
1692 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1693 line.yMax - line.yMin ) );
1697 /* Get rid of any text processed - if necessary repositioning
1698 * at the start of a new line of text
1702 *psz_unicode_start = '\0';
1707 for( i=0; psz_unicode[ i ]; i++ )
1708 psz_unicode_start[ i ] = psz_unicode[ i ];
1709 psz_unicode_start[ i ] = '\0';
1715 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1716 font_stack_t **p_fonts, int i_scale )
1719 char *psz_fontname = NULL;
1720 uint32_t i_font_color = 0xffffff;
1721 int i_font_alpha = 0;
1722 uint32_t i_karaoke_bg_color = 0x00ffffff;
1723 int i_font_size = 24;
1725 /* Default all attributes to the top font in the stack -- in case not
1726 * all attributes are specified in the sub-font
1728 if( VLC_SUCCESS == PeekFont( p_fonts,
1732 &i_karaoke_bg_color ))
1734 psz_fontname = strdup( psz_fontname );
1735 i_font_size = i_font_size * 1000 / i_scale;
1737 i_font_alpha = (i_font_color >> 24) & 0xff;
1738 i_font_color &= 0x00ffffff;
1740 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1742 char *psz_name = xml_ReaderName( p_xml_reader );
1743 char *psz_value = xml_ReaderValue( p_xml_reader );
1745 if( psz_name && psz_value )
1747 if( !strcasecmp( "face", psz_name ) )
1749 if( psz_fontname ) free( psz_fontname );
1750 psz_fontname = strdup( psz_value );
1752 else if( !strcasecmp( "size", psz_name ) )
1754 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1756 int i_value = atoi( psz_value );
1758 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1759 i_font_size += ( i_value * i_font_size ) / 10;
1760 else if( i_value < -5 )
1761 i_font_size = - i_value;
1762 else if( i_value > 5 )
1763 i_font_size = i_value;
1766 i_font_size = atoi( psz_value );
1768 else if( !strcasecmp( "color", psz_name ) &&
1769 ( psz_value[0] == '#' ) )
1771 i_font_color = strtol( psz_value + 1, NULL, 16 );
1772 i_font_color &= 0x00ffffff;
1774 else if( !strcasecmp( "alpha", psz_name ) &&
1775 ( psz_value[0] == '#' ) )
1777 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1778 i_font_alpha &= 0xff;
1784 rv = PushFont( p_fonts,
1786 i_font_size * i_scale / 1000,
1787 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1788 i_karaoke_bg_color );
1790 free( psz_fontname );
1795 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1796 uint32_t **psz_text_out, uint32_t *pi_runs,
1797 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1798 ft_style_t *p_style )
1800 uint32_t i_string_length = 0;
1802 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1803 *psz_text_out += i_string_length;
1805 if( ppp_styles && ppi_run_lengths )
1811 *ppp_styles = (ft_style_t **)
1812 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1814 else if( *pi_runs == 1 )
1816 *ppp_styles = (ft_style_t **)
1817 malloc( *pi_runs * sizeof( ft_style_t * ) );
1820 /* We have just malloc'ed this memory successfully -
1821 * *pi_runs HAS to be within the memory area of *ppp_styles */
1824 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1828 if( *ppi_run_lengths )
1830 *ppi_run_lengths = (uint32_t *)
1831 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1833 else if( *pi_runs == 1 )
1835 *ppi_run_lengths = (uint32_t *)
1836 malloc( *pi_runs * sizeof( uint32_t ) );
1839 /* same remarks here */
1840 if( *ppi_run_lengths )
1842 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1845 /* If we couldn't use the p_style argument due to memory allocation
1846 * problems above, release it here.
1848 if( p_style ) DeleteStyle( p_style );
1851 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1852 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1854 /* Karaoke tags _PRECEDE_ the text they specify a duration
1855 * for, therefore we are working out the length for the
1856 * previous tag, and first time through we have nothing
1858 if( pi_k_run_lengths )
1863 /* Work out how many characters are presently in the string
1865 for( i = 0; i < i_runs; i++ )
1866 i_chars += pi_run_lengths[ i ];
1868 /* Subtract away those we've already allocated to other
1871 for( i = 0; i < i_k_runs; i++ )
1872 i_chars -= pi_k_run_lengths[ i ];
1874 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1878 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1879 uint32_t **ppi_k_run_lengths,
1880 uint32_t **ppi_k_durations )
1882 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1884 char *psz_name = xml_ReaderName( p_xml_reader );
1885 char *psz_value = xml_ReaderValue( p_xml_reader );
1887 if( psz_name && psz_value &&
1888 !strcasecmp( "t", psz_name ) )
1890 if( ppi_k_durations && ppi_k_run_lengths )
1894 if( *ppi_k_durations )
1896 *ppi_k_durations = (uint32_t *)
1897 realloc( *ppi_k_durations,
1898 *pi_k_runs * sizeof( uint32_t ) );
1900 else if( *pi_k_runs == 1 )
1902 *ppi_k_durations = (uint32_t *)
1903 malloc( *pi_k_runs * sizeof( uint32_t ) );
1906 if( *ppi_k_run_lengths )
1908 *ppi_k_run_lengths = (uint32_t *)
1909 realloc( *ppi_k_run_lengths,
1910 *pi_k_runs * sizeof( uint32_t ) );
1912 else if( *pi_k_runs == 1 )
1914 *ppi_k_run_lengths = (uint32_t *)
1915 malloc( *pi_k_runs * sizeof( uint32_t ) );
1917 if( *ppi_k_durations )
1918 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1920 if( *ppi_k_run_lengths )
1921 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1924 if( psz_name ) free( psz_name );
1925 if( psz_value ) free( psz_value );
1929 static int ProcessNodes( filter_t *p_filter,
1930 xml_reader_t *p_xml_reader,
1931 text_style_t *p_font_style,
1936 uint32_t **ppi_run_lengths,
1937 ft_style_t ***ppp_styles,
1939 vlc_bool_t b_karaoke,
1940 uint32_t *pi_k_runs,
1941 uint32_t **ppi_k_run_lengths,
1942 uint32_t **ppi_k_durations )
1944 int rv = VLC_SUCCESS;
1945 filter_sys_t *p_sys = p_filter->p_sys;
1946 uint32_t *psz_text_orig = psz_text;
1947 font_stack_t *p_fonts = NULL;
1951 char *psz_node = NULL;
1953 vlc_bool_t b_italic = VLC_FALSE;
1954 vlc_bool_t b_bold = VLC_FALSE;
1955 vlc_bool_t b_uline = VLC_FALSE;
1957 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1958 i_scale = val.i_int;
1962 rv = PushFont( &p_fonts,
1963 p_font_style->psz_fontname,
1964 p_font_style->i_font_size * i_scale / 1000,
1965 (p_font_style->i_font_color & 0xffffff) |
1966 ((p_font_style->i_font_alpha & 0xff) << 24),
1967 (p_font_style->i_karaoke_background_color & 0xffffff) |
1968 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1970 if( p_font_style->i_style_flags & STYLE_BOLD )
1972 if( p_font_style->i_style_flags & STYLE_ITALIC )
1973 b_italic = VLC_TRUE;
1974 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1979 rv = PushFont( &p_fonts,
1985 if( rv != VLC_SUCCESS )
1988 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1990 switch ( xml_ReaderNodeType( p_xml_reader ) )
1992 case XML_READER_NONE:
1994 case XML_READER_ENDELEM:
1995 psz_node = xml_ReaderName( p_xml_reader );
1999 if( !strcasecmp( "font", psz_node ) )
2000 PopFont( &p_fonts );
2001 else if( !strcasecmp( "b", psz_node ) )
2003 else if( !strcasecmp( "i", psz_node ) )
2004 b_italic = VLC_FALSE;
2005 else if( !strcasecmp( "u", psz_node ) )
2006 b_uline = VLC_FALSE;
2011 case XML_READER_STARTELEM:
2012 psz_node = xml_ReaderName( p_xml_reader );
2015 if( !strcasecmp( "font", psz_node ) )
2016 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2017 else if( !strcasecmp( "b", psz_node ) )
2019 else if( !strcasecmp( "i", psz_node ) )
2020 b_italic = VLC_TRUE;
2021 else if( !strcasecmp( "u", psz_node ) )
2023 else if( !strcasecmp( "br", psz_node ) )
2025 SetupLine( p_filter, "\n", &psz_text,
2026 pi_runs, ppi_run_lengths, ppp_styles,
2027 GetStyleFromFontStack( p_sys,
2033 else if( !strcasecmp( "k", psz_node ) )
2035 /* Only valid in karaoke */
2038 if( *pi_k_runs > 0 )
2040 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2041 *pi_k_runs, *ppi_k_run_lengths );
2043 SetupKaraoke( p_xml_reader, pi_k_runs,
2044 ppi_k_run_lengths, ppi_k_durations );
2051 case XML_READER_TEXT:
2052 psz_node = xml_ReaderValue( p_xml_reader );
2055 /* Turn any multiple-whitespaces into single spaces */
2056 char *s = strpbrk( psz_node, "\t\r\n " );
2059 int i_whitespace = strspn( s, "\t\r\n " );
2061 if( i_whitespace > 1 )
2064 strlen( s ) - i_whitespace + 1 );
2067 s = strpbrk( s, "\t\r\n " );
2069 SetupLine( p_filter, psz_node, &psz_text,
2070 pi_runs, ppi_run_lengths, ppp_styles,
2071 GetStyleFromFontStack( p_sys,
2080 if( rv != VLC_SUCCESS )
2082 psz_text = psz_text_orig;
2088 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2089 *pi_k_runs, *ppi_k_run_lengths );
2092 *pi_len = psz_text - psz_text_orig;
2094 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2099 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2103 for( k=0; k < p_sys->i_font_attachments; k++ )
2105 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2107 FT_Face p_face = NULL;
2109 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2117 vlc_bool_t match = !strcasecmp( p_face->family_name,
2118 p_style->psz_fontname );
2120 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2121 match = match && p_style->b_bold;
2123 match = match && !p_style->b_bold;
2125 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2126 match = match && p_style->b_italic;
2128 match = match && !p_style->b_italic;
2136 FT_Done_Face( p_face );
2141 return VLC_EGENERIC;
2144 static int ProcessLines( filter_t *p_filter,
2149 uint32_t *pi_run_lengths,
2150 ft_style_t **pp_styles,
2151 line_desc_t **pp_lines,
2153 FT_Vector *p_result,
2155 vlc_bool_t b_karaoke,
2157 uint32_t *pi_k_run_lengths,
2158 uint32_t *pi_k_durations )
2160 filter_sys_t *p_sys = p_filter->p_sys;
2161 ft_style_t **pp_char_styles;
2162 int *p_new_positions = NULL;
2163 int8_t *p_levels = NULL;
2164 uint8_t *pi_karaoke_bar = NULL;
2168 /* Assign each character in the text string its style explicitly, so that
2169 * after the characters have been shuffled around by Fribidi, we can re-apply
2170 * the styles, and to simplify the calculation of runs within a line.
2172 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2173 if( !pp_char_styles )
2178 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2179 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2180 * we just won't be able to display the progress bar; at least we'll
2186 for( j = 0; j < i_runs; j++ )
2187 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2188 pp_char_styles[ i++ ] = pp_styles[ j ];
2190 #if defined(HAVE_FRIBIDI)
2192 ft_style_t **pp_char_styles_new;
2193 int *p_old_positions;
2194 uint32_t *p_fribidi_string;
2195 int start_pos, pos = 0;
2197 pp_char_styles_new = (ft_style_t **)
2198 malloc( i_len * sizeof( ft_style_t * ));
2200 p_fribidi_string = (uint32_t *)
2201 malloc( (i_len + 1) * sizeof(uint32_t) );
2202 p_old_positions = (int *)
2203 malloc( (i_len + 1) * sizeof( int ) );
2204 p_new_positions = (int *)
2205 malloc( (i_len + 1) * sizeof( int ) );
2206 p_levels = (int8_t *)
2207 malloc( (i_len + 1) * sizeof( int8_t ) );
2209 if( ! pp_char_styles_new ||
2210 ! p_fribidi_string ||
2211 ! p_old_positions ||
2212 ! p_new_positions ||
2215 msg_Err( p_filter, "out of memory" );
2216 if( p_levels ) free( p_levels );
2217 if( p_old_positions ) free( p_old_positions );
2218 if( p_new_positions ) free( p_new_positions );
2219 if( p_fribidi_string ) free( p_fribidi_string );
2220 if( pp_char_styles_new ) free( pp_char_styles_new );
2221 if( pi_karaoke_bar ) free( pi_karaoke_bar );
2223 free( pp_char_styles );
2227 /* Do bidi conversion line-by-line */
2230 while(pos < i_len) {
2231 if (psz_text[pos] != '\n')
2233 p_fribidi_string[pos] = psz_text[pos];
2234 pp_char_styles_new[pos] = pp_char_styles[pos];
2235 p_new_positions[pos] = pos;
2240 while(pos < i_len) {
2241 if (psz_text[pos] == '\n')
2245 if (pos > start_pos)
2247 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2248 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2249 pos - start_pos, &base_dir,
2250 (FriBidiChar*)p_fribidi_string + start_pos,
2251 p_new_positions + start_pos,
2253 p_levels + start_pos );
2254 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2256 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2257 p_old_positions[ j - start_pos ] ];
2258 p_new_positions[ j ] += start_pos;
2262 free( p_old_positions );
2263 free( pp_char_styles );
2264 pp_char_styles = pp_char_styles_new;
2265 psz_text = p_fribidi_string;
2266 p_fribidi_string[ i_len ] = 0;
2269 /* Work out the karaoke */
2270 if( pi_karaoke_bar )
2272 int64_t i_last_duration = 0;
2273 int64_t i_duration = 0;
2274 int64_t i_start_pos = 0;
2275 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2277 for( k = 0; k< i_k_runs; k++ )
2279 double fraction = 0.0;
2281 i_duration += pi_k_durations[ k ];
2283 if( i_duration < i_elapsed )
2285 /* Completely finished this run-length -
2286 * let it render normally */
2290 else if( i_elapsed < i_last_duration )
2292 /* Haven't got up to this segment yet -
2293 * render it completely in karaoke BG mode */
2299 /* Partway through this run */
2301 fraction = (double)(i_elapsed - i_last_duration) /
2302 (double)pi_k_durations[ k ];
2304 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2306 double shade = pi_k_run_lengths[ k ] * fraction;
2308 if( p_new_positions )
2309 j = p_new_positions[ i_start_pos + i ];
2311 j = i_start_pos + i;
2313 if( i < (uint32_t)shade )
2314 pi_karaoke_bar[ j ] = 0xff;
2315 else if( (double)i > shade )
2316 pi_karaoke_bar[ j ] = 0x00;
2319 shade -= (int)shade;
2320 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2321 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2325 i_last_duration = i_duration;
2326 i_start_pos += pi_k_run_lengths[ k ];
2329 if( p_levels ) free( p_levels );
2330 if( p_new_positions ) free( p_new_positions );
2332 FT_Vector tmp_result;
2334 line_desc_t *p_line = NULL;
2335 line_desc_t *p_prev = NULL;
2341 p_result->x = p_result->y = 0;
2342 tmp_result.x = tmp_result.y = 0;
2345 for( k = 0; k <= (uint32_t) i_len; k++ )
2347 if( ( k == (uint32_t) i_len ) ||
2349 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2351 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2353 /* End of the current style run */
2354 FT_Face p_face = NULL;
2357 /* Look for a match amongst our attachments first */
2358 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2360 if( ! p_face && p_sys->b_fontconfig_ok )
2364 vlc_mutex_lock( &p_sys->fontconfig_lock );
2366 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2367 p_style->psz_fontname,
2372 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2376 if( FT_New_Face( p_sys->p_library,
2377 psz_fontfile, i_idx, &p_face ) )
2379 free( psz_fontfile );
2380 free( pp_char_styles );
2381 #if defined(HAVE_FRIBIDI)
2384 if( pi_karaoke_bar )
2385 free( pi_karaoke_bar );
2386 return VLC_EGENERIC;
2388 free( psz_fontfile );
2392 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2394 /* We've loaded a font face which is unhelpful for actually
2395 * rendering text - fallback to the default one.
2397 FT_Done_Face( p_face );
2401 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2402 ft_encoding_unicode ) ||
2403 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2404 p_style->i_font_size ) )
2406 if( p_face ) FT_Done_Face( p_face );
2407 free( pp_char_styles );
2408 #if defined(HAVE_FRIBIDI)
2411 if( pi_karaoke_bar )
2412 free( pi_karaoke_bar );
2413 return VLC_EGENERIC;
2415 p_sys->i_use_kerning =
2416 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2419 uint32_t *psz_unicode = (uint32_t *)
2420 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2423 msg_Err( p_filter, "out of memory" );
2424 if( p_face ) FT_Done_Face( p_face );
2425 free( pp_char_styles );
2426 free( psz_unicode );
2427 #if defined(HAVE_FRIBIDI)
2430 if( pi_karaoke_bar )
2431 free( pi_karaoke_bar );
2434 memcpy( psz_unicode, psz_text + i_prev,
2435 sizeof( uint32_t ) * ( k - i_prev ) );
2436 psz_unicode[ k - i_prev ] = 0;
2437 while( *psz_unicode )
2441 if( !(p_line = NewLine( i_len - i_prev)) )
2443 msg_Err( p_filter, "out of memory" );
2444 if( p_face ) FT_Done_Face( p_face );
2445 free( pp_char_styles );
2446 free( psz_unicode );
2447 #if defined(HAVE_FRIBIDI)
2450 if( pi_karaoke_bar )
2451 free( pi_karaoke_bar );
2454 /* New Color mode only works in YUVA rendering mode --
2455 * (RGB mode has palette constraints on it). We therefore
2456 * need to populate the legacy colour fields also.
2458 p_line->b_new_color_mode = VLC_TRUE;
2459 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2460 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2461 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2462 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2463 p_line->p_next = NULL;
2465 i_pen_y += tmp_result.y;
2469 if( p_prev ) p_prev->p_next = p_line;
2470 else *pp_lines = p_line;
2473 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2474 p_style->i_font_color, p_style->b_underline,
2475 p_style->i_karaoke_bg_color,
2476 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2477 &tmp_result ) != VLC_SUCCESS )
2479 if( p_face ) FT_Done_Face( p_face );
2480 free( pp_char_styles );
2481 free( psz_unicode );
2482 #if defined(HAVE_FRIBIDI)
2485 if( pi_karaoke_bar )
2486 free( pi_karaoke_bar );
2487 return VLC_EGENERIC;
2492 p_result->x = __MAX( p_result->x, tmp_result.x );
2493 p_result->y += tmp_result.y;
2499 free( psz_unicode );
2500 if( p_face ) FT_Done_Face( p_face );
2504 free( pp_char_styles );
2505 #if defined(HAVE_FRIBIDI)
2510 p_result->x = __MAX( p_result->x, tmp_result.x );
2511 p_result->y += tmp_result.y;
2514 if( pi_karaoke_bar )
2517 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2519 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2521 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2525 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2527 /* 100% BG colour will render faster if we
2528 * instead make it 100% FG colour, so leave
2529 * the ratio alone and copy the value across
2531 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2535 if( pi_karaoke_bar[ i ] & 0x80 )
2537 /* Swap Left and Right sides over for Right aligned
2538 * language text (eg. Arabic, Hebrew)
2540 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2542 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2543 p_line->p_bg_rgb[ k ] = i_tmp;
2545 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2548 /* Jump over the '\n' at the line-end */
2551 free( pi_karaoke_bar );
2557 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2558 subpicture_region_t *p_region_in )
2560 int rv = VLC_SUCCESS;
2561 stream_t *p_sub = NULL;
2562 xml_t *p_xml = NULL;
2563 xml_reader_t *p_xml_reader = NULL;
2565 if( !p_region_in || !p_region_in->psz_html )
2566 return VLC_EGENERIC;
2568 /* Reset the default fontsize in case screen metrics have changed */
2569 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2571 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2572 (uint8_t *) p_region_in->psz_html,
2573 strlen( p_region_in->psz_html ),
2577 p_xml = xml_Create( p_filter );
2580 vlc_bool_t b_karaoke = VLC_FALSE;
2582 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2585 /* Look for Root Node */
2586 if( xml_ReaderRead( p_xml_reader ) == 1 )
2588 char *psz_node = xml_ReaderName( p_xml_reader );
2590 if( !strcasecmp( "karaoke", psz_node ) )
2592 /* We're going to have to render the text a number
2593 * of times to show the progress marker on the text.
2595 var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2596 b_karaoke = VLC_TRUE;
2598 else if( !strcasecmp( "text", psz_node ) )
2600 b_karaoke = VLC_FALSE;
2604 /* Only text and karaoke tags are supported */
2605 xml_ReaderDelete( p_xml, p_xml_reader );
2606 p_xml_reader = NULL;
2618 uint32_t i_runs = 0;
2619 uint32_t i_k_runs = 0;
2620 uint32_t *pi_run_lengths = NULL;
2621 uint32_t *pi_k_run_lengths = NULL;
2622 uint32_t *pi_k_durations = NULL;
2623 ft_style_t **pp_styles = NULL;
2625 line_desc_t *p_lines = NULL;
2627 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2628 sizeof( uint32_t ) );
2633 rv = ProcessNodes( p_filter, p_xml_reader,
2634 p_region_in->p_style, psz_text, &i_len,
2635 &i_runs, &pi_run_lengths, &pp_styles,
2636 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2639 p_region_out->i_x = p_region_in->i_x;
2640 p_region_out->i_y = p_region_in->i_y;
2642 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2644 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2645 pi_run_lengths, pp_styles, &p_lines, &result,
2646 b_karaoke, i_k_runs, pi_k_run_lengths,
2650 for( k=0; k<i_runs; k++)
2651 DeleteStyle( pp_styles[k] );
2653 free( pi_run_lengths );
2656 /* Don't attempt to render text that couldn't be layed out
2659 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2661 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2663 Render( p_filter, p_region_out, p_lines,
2664 result.x, result.y );
2668 RenderYUVA( p_filter, p_region_out, p_lines,
2669 result.x, result.y );
2673 FreeLines( p_lines );
2675 xml_ReaderDelete( p_xml, p_xml_reader );
2677 xml_Delete( p_xml );
2679 stream_Delete( p_sub );
2685 static char* FontConfig_Select( FcConfig* priv, const char* family,
2686 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2689 FcPattern *pat, *p_pat;
2693 pat = FcPatternCreate();
2694 if (!pat) return NULL;
2696 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2697 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2698 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2699 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2701 FcDefaultSubstitute( pat );
2703 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2705 FcPatternDestroy( pat );
2709 p_pat = FcFontMatch( priv, pat, &result );
2710 FcPatternDestroy( pat );
2711 if( !p_pat ) return NULL;
2713 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2714 || ( val_b != FcTrue ) )
2716 FcPatternDestroy( p_pat );
2719 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2724 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2726 FcPatternDestroy( p_pat );
2731 if( strcasecmp((const char*)val_s, family ) != 0 )
2732 msg_Warn( p_filter, "fontconfig: selected font family is not"
2733 "the requested one: '%s' != '%s'\n",
2734 (const char*)val_s, family );
2737 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2739 FcPatternDestroy( p_pat );
2743 FcPatternDestroy( p_pat );
2744 return strdup( (const char*)val_s );
2748 static void FreeLine( line_desc_t *p_line )
2751 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2753 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2755 free( p_line->pp_glyphs );
2756 free( p_line->p_glyph_pos );
2757 free( p_line->p_fg_rgb );
2758 free( p_line->p_bg_rgb );
2759 free( p_line->p_fg_bg_ratio );
2760 free( p_line->pi_underline_offset );
2761 free( p_line->pi_underline_thickness );
2765 static void FreeLines( line_desc_t *p_lines )
2767 line_desc_t *p_line, *p_next;
2769 if( !p_lines ) return;
2771 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2773 p_next = p_line->p_next;
2778 static line_desc_t *NewLine( int i_count )
2780 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2782 if( !p_line ) return NULL;
2783 p_line->i_height = 0;
2784 p_line->i_width = 0;
2785 p_line->p_next = NULL;
2787 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2788 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2789 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2790 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2791 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2792 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2793 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2794 if( ( p_line->pp_glyphs == NULL ) ||
2795 ( p_line->p_glyph_pos == NULL ) ||
2796 ( p_line->p_fg_rgb == NULL ) ||
2797 ( p_line->p_bg_rgb == NULL ) ||
2798 ( p_line->p_fg_bg_ratio == NULL ) ||
2799 ( p_line->pi_underline_offset == NULL ) ||
2800 ( p_line->pi_underline_thickness == NULL ) )
2802 if( p_line->pi_underline_thickness )
2803 free( p_line->pi_underline_thickness );
2804 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2805 if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2806 if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2807 if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2808 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2809 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2813 p_line->pp_glyphs[0] = NULL;
2814 p_line->b_new_color_mode = VLC_FALSE;
2819 static int GetFontSize( filter_t *p_filter )
2821 filter_sys_t *p_sys = p_filter->p_sys;
2825 if( p_sys->i_default_font_size )
2827 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2828 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2830 i_size = p_sys->i_default_font_size;
2834 var_Get( p_filter, "freetype-rel-fontsize", &val );
2835 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2836 p_filter->p_sys->i_display_height =
2837 p_filter->fmt_out.video.i_height;
2841 msg_Warn( p_filter, "invalid fontsize, using 12" );
2842 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2843 i_size = 12 * val.i_int / 1000;
2850 static int SetFontSize( filter_t *p_filter, int i_size )
2852 filter_sys_t *p_sys = p_filter->p_sys;
2856 i_size = GetFontSize( p_filter );
2858 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2861 p_sys->i_font_size = i_size;
2863 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2865 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2866 return VLC_EGENERIC;
2872 static void YUVFromRGB( uint32_t i_argb,
2873 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2875 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2876 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2877 int i_blue = ( i_argb & 0x000000ff );
2879 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2880 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2881 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2882 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2883 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2884 -585 * i_blue + 4096 + 1048576) >> 13, 240);