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 i_error = FT_Init_FreeType( &p_sys->p_library );
340 msg_Err( p_filter, "couldn't initialize freetype" );
343 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
345 if( i_error == FT_Err_Unknown_File_Format )
347 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
352 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
356 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
359 msg_Err( p_filter, "font has no unicode translation table" );
363 #ifdef HAVE_FONTCONFIG
364 vlc_mutex_init( p_filter, &p_sys->fontconfig_lock );
365 p_sys->b_fontconfig_ok = VLC_FALSE;
367 p_sys->p_fontconfig = FcInitLoadConfig();
369 if( p_sys->p_fontconfig )
371 /* Normally this doesn't take very long, but an initial build of
372 * the fontconfig database or the addition of a lot of new fonts
373 * can cause it to take several minutes for a large number of fonts.
374 * Even a small number can take several seconds - much longer than
375 * we can afford to block, so we build the list in the background
376 * and if it succeeds we allow fontconfig to be used.
378 if( vlc_thread_create( p_filter, "fontlist builder", FontBuilder,
379 VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
381 /* Don't destroy the fontconfig object - we won't be able to do
382 * italics or bold or change the font face, but we will still
383 * be able to do underline and change the font size.
385 msg_Warn( p_filter, "fontconfig database builder thread can't "
386 "be launched. Font styling support will be limited." );
391 msg_Warn( p_filter, "Couldn't initialise Fontconfig. "
392 "Font styling won't be available." );
396 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
398 var_Get( p_filter, "freetype-fontsize", &val );
399 p_sys->i_default_font_size = val.i_int;
400 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
402 if( psz_fontfile ) free( psz_fontfile );
404 p_sys->pp_font_attachments = NULL;
405 p_sys->i_font_attachments = 0;
407 p_filter->pf_render_text = RenderText;
408 #ifdef HAVE_FONTCONFIG
409 if( p_sys->p_fontconfig )
410 p_filter->pf_render_html = RenderHtml;
413 p_filter->pf_render_html = NULL;
415 LoadFontsFromAttachments( p_filter );
420 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
421 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
422 if( psz_fontfile ) free( psz_fontfile );
427 /*****************************************************************************
428 * Destroy: destroy Clone video thread output method
429 *****************************************************************************
430 * Clean up all data and library connections
431 *****************************************************************************/
432 static void Destroy( vlc_object_t *p_this )
434 filter_t *p_filter = (filter_t *)p_this;
435 filter_sys_t *p_sys = p_filter->p_sys;
437 if( p_sys->pp_font_attachments )
441 for( k = 0; k < p_sys->i_font_attachments; k++ )
443 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
446 free( p_sys->pp_font_attachments );
449 #ifdef HAVE_FONTCONFIG
450 /* wait for the FontBuilder thread to terminate */
451 vlc_thread_join( p_this );
452 vlc_mutex_destroy( &p_sys->fontconfig_lock );
454 if( p_sys->p_fontconfig )
456 FcConfigDestroy( p_sys->p_fontconfig );
457 p_sys->p_fontconfig = NULL;
459 /* FcFini asserts calling the subfunction FcCacheFini()
460 * even if no other library functions have been made since FcInit(),
461 * so don't call it. */
463 FT_Done_Face( p_sys->p_face );
464 FT_Done_FreeType( p_sys->p_library );
468 #ifdef HAVE_FONTCONFIG
470 static void FontBuilder( vlc_object_t *p_this )
472 filter_t *p_filter = (filter_t*)p_this;
473 filter_sys_t *p_sys = p_filter->p_sys;
476 /* Find the session to announce */
477 vlc_mutex_lock( &p_sys->fontconfig_lock );
479 msg_Dbg( p_filter, "Building font database..." );
481 if(! FcConfigBuildFonts( p_sys->p_fontconfig ))
483 /* Don't destroy the fontconfig object - we won't be able to do
484 * italics or bold or change the font face, but we will still
485 * be able to do underline and change the font size.
487 msg_Err( p_filter, "fontconfig database can't be built. "
488 "Font styling won't be available" );
492 msg_Dbg( p_filter, "Finished building font database." );
493 if( t1 > 0 && t2 > 0 )
494 msg_Dbg( p_filter, "Took %ld seconds", t2 - t1 );
496 p_sys->b_fontconfig_ok = VLC_TRUE;
498 vlc_mutex_unlock( &p_sys->fontconfig_lock );
503 /*****************************************************************************
504 * Make any TTF/OTF fonts present in the attachments of the media file
505 * and store them for later use by the FreeType Engine
506 *****************************************************************************/
507 static int LoadFontsFromAttachments( filter_t *p_filter )
509 filter_sys_t *p_sys = p_filter->p_sys;
510 input_thread_t *p_input;
511 input_attachment_t **pp_attachments;
512 int i_attachments_cnt;
514 int rv = VLC_SUCCESS;
516 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
520 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
522 vlc_object_release(p_input);
526 p_sys->i_font_attachments = 0;
527 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
528 if(! p_sys->pp_font_attachments )
531 for( k = 0; k < i_attachments_cnt; k++ )
533 input_attachment_t *p_attach = pp_attachments[k];
535 if( p_sys->pp_font_attachments )
537 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
538 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
539 ( p_attach->i_data > 0 ) &&
540 ( p_attach->p_data != NULL ) )
542 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
546 vlc_input_attachment_Delete( p_attach );
551 vlc_input_attachment_Delete( p_attach );
554 free( pp_attachments );
556 vlc_object_release(p_input);
561 /*****************************************************************************
562 * Render: place string in picture
563 *****************************************************************************
564 * This function merges the previously rendered freetype glyphs into a picture
565 *****************************************************************************/
566 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
567 line_desc_t *p_line, int i_width, int i_height )
569 static uint8_t pi_gamma[16] =
570 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
571 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
575 int i, x, y, i_pitch;
576 uint8_t i_y; /* YUV values, derived from incoming RGB */
578 subpicture_region_t *p_region_tmp;
580 /* Create a new subpicture region */
581 memset( &fmt, 0, sizeof(video_format_t) );
582 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
584 fmt.i_width = fmt.i_visible_width = i_width + 4;
585 fmt.i_height = fmt.i_visible_height = i_height + 4;
586 if( p_region->fmt.i_visible_width > 0 )
587 fmt.i_visible_width = p_region->fmt.i_visible_width;
588 if( p_region->fmt.i_visible_height > 0 )
589 fmt.i_visible_height = p_region->fmt.i_visible_height;
590 fmt.i_x_offset = fmt.i_y_offset = 0;
591 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
594 msg_Err( p_filter, "cannot allocate SPU region" );
598 p_region->fmt = p_region_tmp->fmt;
599 p_region->picture = p_region_tmp->picture;
600 free( p_region_tmp );
602 /* Calculate text color components */
603 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
604 25 * p_line->i_blue + 128) >> 8) + 16;
605 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
606 112 * p_line->i_blue + 128) >> 8) + 128;
607 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
608 18 * p_line->i_blue + 128) >> 8) + 128;
611 fmt.p_palette->i_entries = 16;
612 for( i = 0; i < 8; i++ )
614 fmt.p_palette->palette[i][0] = 0;
615 fmt.p_palette->palette[i][1] = 0x80;
616 fmt.p_palette->palette[i][2] = 0x80;
617 fmt.p_palette->palette[i][3] = pi_gamma[i];
618 fmt.p_palette->palette[i][3] =
619 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
621 for( i = 8; i < fmt.p_palette->i_entries; i++ )
623 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
624 fmt.p_palette->palette[i][1] = i_u;
625 fmt.p_palette->palette[i][2] = i_v;
626 fmt.p_palette->palette[i][3] = pi_gamma[i];
627 fmt.p_palette->palette[i][3] =
628 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
631 p_dst = p_region->picture.Y_PIXELS;
632 i_pitch = p_region->picture.Y_PITCH;
634 /* Initialize the region pixels */
635 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
637 for( ; p_line != NULL; p_line = p_line->p_next )
639 int i_glyph_tmax = 0;
640 int i_bitmap_offset, i_offset, i_align_offset = 0;
641 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
643 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
644 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
647 if( p_line->i_width < i_width )
649 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
651 i_align_offset = i_width - p_line->i_width;
653 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
655 i_align_offset = ( i_width - p_line->i_width ) / 2;
659 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
661 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
663 i_offset = ( p_line->p_glyph_pos[ i ].y +
664 i_glyph_tmax - p_glyph->top + 2 ) *
665 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
668 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
670 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
672 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
674 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
681 /* Outlining (find something better than nearest neighbour filtering ?) */
684 uint8_t *p_dst = p_region->picture.Y_PIXELS;
685 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
686 uint8_t left, current;
688 for( y = 1; y < (int)fmt.i_height - 1; y++ )
690 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
691 p_dst += p_region->picture.Y_PITCH;
694 for( x = 1; x < (int)fmt.i_width - 1; x++ )
697 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
698 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;
702 memset( p_top, 0, fmt.i_width );
708 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
709 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
710 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
711 int i_glyph_tmax, int i_align_offset,
712 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
713 subpicture_region_t *p_region)
717 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
719 p_dst_y = p_region->picture.Y_PIXELS;
720 p_dst_u = p_region->picture.U_PIXELS;
721 p_dst_v = p_region->picture.V_PIXELS;
722 p_dst_a = p_region->picture.A_PIXELS;
723 i_pitch = p_region->picture.A_PITCH;
725 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
726 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
728 for( y = 0; y < i_line_thickness; y++ )
730 int i_extra = p_this_glyph->bitmap.width;
734 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
735 (p_this_glyph_pos->x + p_this_glyph->left);
737 for( x = 0; x < i_extra; x++ )
739 vlc_bool_t b_ok = VLC_TRUE;
741 /* break the underline around the tails of any glyphs which cross it */
742 for( z = x - i_line_thickness;
743 z < x + i_line_thickness && b_ok;
746 if( p_next_glyph && ( z >= i_extra ) )
748 int i_row = i_line_offset + p_next_glyph->top + y;
750 if( ( p_next_glyph->bitmap.rows > i_row ) &&
751 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
756 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
758 int i_row = i_line_offset + p_this_glyph->top + y;
760 if( ( p_this_glyph->bitmap.rows > i_row ) &&
761 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
770 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
771 p_dst_u[i_offset+x] = i_u;
772 p_dst_v[i_offset+x] = i_v;
773 p_dst_a[i_offset+x] = 255;
780 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
782 uint8_t *p_dst = p_region->picture.A_PIXELS;
783 int i_pitch = p_region->picture.A_PITCH;
786 for( ; p_line != NULL; p_line = p_line->p_next )
788 int i_glyph_tmax=0, i = 0;
789 int i_bitmap_offset, i_offset, i_align_offset = 0;
790 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
792 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
793 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
796 if( p_line->i_width < i_width )
798 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
800 i_align_offset = i_width - p_line->i_width;
802 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
804 i_align_offset = ( i_width - p_line->i_width ) / 2;
808 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
810 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
812 i_offset = ( p_line->p_glyph_pos[ i ].y +
813 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
814 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
815 i_align_offset +xoffset;
817 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
819 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
821 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
822 if( p_dst[i_offset+x] <
823 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
825 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
834 /*****************************************************************************
835 * Render: place string in picture
836 *****************************************************************************
837 * This function merges the previously rendered freetype glyphs into a picture
838 *****************************************************************************/
839 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
840 line_desc_t *p_line, int i_width, int i_height )
842 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
844 int i, x, y, i_pitch, i_alpha;
845 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
846 subpicture_region_t *p_region_tmp;
848 if( i_width == 0 || i_height == 0 )
851 /* Create a new subpicture region */
852 memset( &fmt, 0, sizeof(video_format_t) );
853 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
855 fmt.i_width = fmt.i_visible_width = i_width + 6;
856 fmt.i_height = fmt.i_visible_height = i_height + 6;
857 if( p_region->fmt.i_visible_width > 0 )
858 fmt.i_visible_width = p_region->fmt.i_visible_width;
859 if( p_region->fmt.i_visible_height > 0 )
860 fmt.i_visible_height = p_region->fmt.i_visible_height;
861 fmt.i_x_offset = fmt.i_y_offset = 0;
862 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
865 msg_Err( p_filter, "cannot allocate SPU region" );
869 p_region->fmt = p_region_tmp->fmt;
870 p_region->picture = p_region_tmp->picture;
871 free( p_region_tmp );
873 /* Calculate text color components */
874 YUVFromRGB( (p_line->i_red << 16) |
875 (p_line->i_green << 8) |
878 i_alpha = p_line->i_alpha;
880 p_dst_y = p_region->picture.Y_PIXELS;
881 p_dst_u = p_region->picture.U_PIXELS;
882 p_dst_v = p_region->picture.V_PIXELS;
883 p_dst_a = p_region->picture.A_PIXELS;
884 i_pitch = p_region->picture.A_PITCH;
886 /* Initialize the region pixels */
887 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
889 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
890 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
891 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
892 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
896 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
897 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
898 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
899 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
901 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
902 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
904 DrawBlack( p_line, i_width, p_region, 0, 0);
905 DrawBlack( p_line, i_width, p_region, -1, 0);
906 DrawBlack( p_line, i_width, p_region, 0, -1);
907 DrawBlack( p_line, i_width, p_region, 1, 0);
908 DrawBlack( p_line, i_width, p_region, 0, 1);
911 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
913 DrawBlack( p_line, i_width, p_region, -1, -1);
914 DrawBlack( p_line, i_width, p_region, -1, 1);
915 DrawBlack( p_line, i_width, p_region, 1, -1);
916 DrawBlack( p_line, i_width, p_region, 1, 1);
918 DrawBlack( p_line, i_width, p_region, -2, 0);
919 DrawBlack( p_line, i_width, p_region, 0, -2);
920 DrawBlack( p_line, i_width, p_region, 2, 0);
921 DrawBlack( p_line, i_width, p_region, 0, 2);
923 DrawBlack( p_line, i_width, p_region, -2, -2);
924 DrawBlack( p_line, i_width, p_region, -2, 2);
925 DrawBlack( p_line, i_width, p_region, 2, -2);
926 DrawBlack( p_line, i_width, p_region, 2, 2);
928 DrawBlack( p_line, i_width, p_region, -3, 0);
929 DrawBlack( p_line, i_width, p_region, 0, -3);
930 DrawBlack( p_line, i_width, p_region, 3, 0);
931 DrawBlack( p_line, i_width, p_region, 0, 3);
934 for( ; p_line != NULL; p_line = p_line->p_next )
936 int i_glyph_tmax = 0;
937 int i_bitmap_offset, i_offset, i_align_offset = 0;
938 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
940 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
941 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
944 if( p_line->i_width < i_width )
946 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
948 i_align_offset = i_width - p_line->i_width;
950 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
952 i_align_offset = ( i_width - p_line->i_width ) / 2;
956 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
958 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
960 i_offset = ( p_line->p_glyph_pos[ i ].y +
961 i_glyph_tmax - p_glyph->top + 3 ) *
962 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
965 if( p_line->b_new_color_mode )
967 /* Every glyph can (and in fact must) have its own color */
968 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
971 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
973 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
975 uint8_t i_y_local = i_y;
976 uint8_t i_u_local = i_u;
977 uint8_t i_v_local = i_v;
979 if( p_line->p_fg_bg_ratio != 0x00 )
981 int i_split = p_glyph->bitmap.width *
982 p_line->p_fg_bg_ratio[ i ] / 0x7f;
986 YUVFromRGB( p_line->p_bg_rgb[ i ],
987 &i_y_local, &i_u_local, &i_v_local );
991 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
993 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
994 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
996 p_dst_u[i_offset+x] = i_u;
997 p_dst_v[i_offset+x] = i_v;
999 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1000 p_dst_a[i_offset+x] = 0xff;
1003 i_offset += i_pitch;
1006 if( p_line->pi_underline_thickness[ i ] )
1008 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1009 p_line->pi_underline_offset[ i ],
1010 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1011 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1012 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1013 i_glyph_tmax, i_align_offset,
1014 i_y, i_u, i_v, i_alpha,
1020 /* Apply the alpha setting */
1021 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1022 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1028 * This function renders a text subpicture region into another one.
1029 * It also calculates the size needed for this string, and renders the
1030 * needed glyphs into memory. It is used as pf_add_string callback in
1031 * the vout method by this module
1033 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1034 subpicture_region_t *p_region_in )
1036 filter_sys_t *p_sys = p_filter->p_sys;
1037 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1038 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1039 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1040 int i_string_length;
1042 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1043 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1053 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1054 psz_string = p_region_in->psz_text;
1055 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1057 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1058 i_scale = val.i_int;
1060 if( p_region_in->p_style )
1062 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1063 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1064 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1068 i_font_color = p_sys->i_font_color;
1069 i_font_alpha = 255 - p_sys->i_font_opacity;
1070 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1073 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1074 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1075 SetFontSize( p_filter, i_font_size );
1077 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1078 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1079 i_blue = i_font_color & 0x000000FF;
1081 result.x = result.y = 0;
1082 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1084 psz_unicode = psz_unicode_orig =
1085 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1086 if( psz_unicode == NULL )
1088 msg_Err( p_filter, "out of memory" );
1091 #if defined(WORDS_BIGENDIAN)
1092 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1094 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1096 if( iconv_handle == (vlc_iconv_t)-1 )
1098 msg_Warn( p_filter, "unable to do conversion" );
1104 const char *p_in_buffer = psz_string;
1105 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1106 i_in_bytes = strlen( psz_string );
1107 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1108 i_out_bytes_left = i_out_bytes;
1109 p_out_buffer = (char *)psz_unicode;
1110 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1111 &p_out_buffer, &i_out_bytes_left );
1113 vlc_iconv_close( iconv_handle );
1117 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1118 "bytes left %u", (unsigned)i_in_bytes );
1121 *(uint32_t*)p_out_buffer = 0;
1122 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1125 #if defined(HAVE_FRIBIDI)
1127 uint32_t *p_fribidi_string;
1128 int start_pos, pos = 0;
1130 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1131 if( !p_fribidi_string )
1133 msg_Err( p_filter, "out of memory" );
1137 /* Do bidi conversion line-by-line */
1138 while(pos < i_string_length)
1140 while(pos < i_string_length) {
1141 i_char = psz_unicode[pos];
1142 if (i_char != '\r' && i_char != '\n')
1144 p_fribidi_string[pos] = i_char;
1148 while(pos < i_string_length) {
1149 i_char = psz_unicode[pos];
1150 if (i_char == '\r' || i_char == '\n')
1154 if (pos > start_pos)
1156 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1157 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
1158 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
1162 free( psz_unicode_orig );
1163 psz_unicode = psz_unicode_orig = p_fribidi_string;
1164 p_fribidi_string[ i_string_length ] = 0;
1168 /* Calculate relative glyph positions and a bounding box for the
1170 if( !(p_line = NewLine( strlen( psz_string ))) )
1172 msg_Err( p_filter, "out of memory" );
1176 i_pen_x = i_pen_y = 0;
1178 psz_line_start = psz_unicode;
1180 #define face p_sys->p_face
1181 #define glyph face->glyph
1183 while( *psz_unicode )
1185 i_char = *psz_unicode++;
1186 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1191 if( i_char == '\n' )
1193 psz_line_start = psz_unicode;
1194 if( !(p_next = NewLine( strlen( psz_string ))) )
1196 msg_Err( p_filter, "out of memory" );
1199 p_line->p_next = p_next;
1200 p_line->i_width = line.xMax;
1201 p_line->i_height = face->size->metrics.height >> 6;
1202 p_line->pp_glyphs[ i ] = NULL;
1203 p_line->i_alpha = i_font_alpha;
1204 p_line->i_red = i_red;
1205 p_line->i_green = i_green;
1206 p_line->i_blue = i_blue;
1209 result.x = __MAX( result.x, line.xMax );
1210 result.y += face->size->metrics.height >> 6;
1213 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1214 i_pen_y += face->size->metrics.height >> 6;
1216 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1221 i_glyph_index = FT_Get_Char_Index( face, i_char );
1222 if( p_sys->i_use_kerning && i_glyph_index
1226 FT_Get_Kerning( face, i_previous, i_glyph_index,
1227 ft_kerning_default, &delta );
1228 i_pen_x += delta.x >> 6;
1231 p_line->p_glyph_pos[ i ].x = i_pen_x;
1232 p_line->p_glyph_pos[ i ].y = i_pen_y;
1233 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1236 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1240 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1243 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1247 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1248 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1251 FT_Done_Glyph( tmp_glyph );
1254 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1257 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1258 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1259 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1261 p_line->pp_glyphs[ i ] = NULL;
1263 p_line = NewLine( strlen( psz_string ));
1264 if( p_prev ) p_prev->p_next = p_line;
1265 else p_lines = p_line;
1267 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1271 if( psz_unicode == psz_line_start )
1273 msg_Warn( p_filter, "unbreakable string" );
1278 *psz_unicode = '\n';
1280 psz_unicode = psz_line_start;
1283 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1286 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1287 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1289 i_previous = i_glyph_index;
1290 i_pen_x += glyph->advance.x >> 6;
1294 p_line->i_width = line.xMax;
1295 p_line->i_height = face->size->metrics.height >> 6;
1296 p_line->pp_glyphs[ i ] = NULL;
1297 p_line->i_alpha = i_font_alpha;
1298 p_line->i_red = i_red;
1299 p_line->i_green = i_green;
1300 p_line->i_blue = i_blue;
1301 result.x = __MAX( result.x, line.xMax );
1302 result.y += line.yMax - line.yMin;
1307 p_region_out->i_x = p_region_in->i_x;
1308 p_region_out->i_y = p_region_in->i_y;
1310 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1311 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1313 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1315 if( psz_unicode_orig ) free( psz_unicode_orig );
1316 FreeLines( p_lines );
1320 if( psz_unicode_orig ) free( psz_unicode_orig );
1321 FreeLines( p_lines );
1322 return VLC_EGENERIC;
1325 #ifdef HAVE_FONTCONFIG
1326 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1327 uint32_t i_font_color, uint32_t i_karaoke_bg_color, vlc_bool_t b_bold,
1328 vlc_bool_t b_italic, vlc_bool_t b_uline )
1330 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1334 p_style->i_font_size = i_font_size;
1335 p_style->i_font_color = i_font_color;
1336 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1337 p_style->b_italic = b_italic;
1338 p_style->b_bold = b_bold;
1339 p_style->b_underline = b_uline;
1341 p_style->psz_fontname = strdup( psz_fontname );
1346 static void DeleteStyle( ft_style_t *p_style )
1350 if( p_style->psz_fontname )
1351 free( p_style->psz_fontname );
1356 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1363 if(( s1->i_font_size == s2->i_font_size ) &&
1364 ( s1->i_font_color == s2->i_font_color ) &&
1365 ( s1->b_italic == s2->b_italic ) &&
1366 ( s1->b_bold == s2->b_bold ) &&
1367 ( s1->b_underline == s2->b_underline ) &&
1368 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1375 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1376 uint32_t i_color, uint32_t i_karaoke_bg_color )
1378 font_stack_t *p_new;
1381 return VLC_EGENERIC;
1383 p_new = malloc( sizeof( font_stack_t ) );
1387 p_new->p_next = NULL;
1390 p_new->psz_name = strdup( psz_name );
1392 p_new->psz_name = NULL;
1394 p_new->i_size = i_size;
1395 p_new->i_color = i_color;
1396 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1404 font_stack_t *p_last;
1406 for( p_last = *p_font;
1408 p_last = p_last->p_next )
1411 p_last->p_next = p_new;
1416 static int PopFont( font_stack_t **p_font )
1418 font_stack_t *p_last, *p_next_to_last;
1420 if( !p_font || !*p_font )
1421 return VLC_EGENERIC;
1423 p_next_to_last = NULL;
1424 for( p_last = *p_font;
1426 p_last = p_last->p_next )
1428 p_next_to_last = p_last;
1431 if( p_next_to_last )
1432 p_next_to_last->p_next = NULL;
1436 free( p_last->psz_name );
1442 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1443 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1445 font_stack_t *p_last;
1447 if( !p_font || !*p_font )
1448 return VLC_EGENERIC;
1450 for( p_last=*p_font;
1452 p_last=p_last->p_next )
1455 *psz_name = p_last->psz_name;
1456 *i_size = p_last->i_size;
1457 *i_color = p_last->i_color;
1458 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1463 static void IconvText( filter_t *p_filter, const char *psz_string,
1464 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1466 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1468 /* If memory hasn't been allocated for our output string, allocate it here
1469 * - the calling function must now be responsible for freeing it.
1471 if( !*ppsz_unicode )
1472 *ppsz_unicode = (uint32_t *)
1473 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1475 /* We don't need to handle a NULL pointer in *ppsz_unicode
1476 * if we are instead testing for a non NULL value like we are here */
1480 #if defined(WORDS_BIGENDIAN)
1481 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1483 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1485 if( iconv_handle != (vlc_iconv_t)-1 )
1487 char *p_in_buffer, *p_out_buffer;
1488 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1489 i_in_bytes = strlen( psz_string );
1490 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1491 i_out_bytes_left = i_out_bytes;
1492 p_in_buffer = (char *) psz_string;
1493 p_out_buffer = (char *) *ppsz_unicode;
1494 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1495 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1497 vlc_iconv_close( iconv_handle );
1501 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1502 "bytes left %u", (unsigned)i_in_bytes );
1506 *(uint32_t*)p_out_buffer = 0;
1508 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1513 msg_Warn( p_filter, "unable to do conversion" );
1518 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1519 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1520 vlc_bool_t b_uline )
1522 ft_style_t *p_style = NULL;
1524 char *psz_fontname = NULL;
1525 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1526 uint32_t i_karaoke_bg_color = i_font_color;
1527 int i_font_size = p_sys->i_font_size;
1529 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1530 &i_font_color, &i_karaoke_bg_color ))
1532 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1533 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1538 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1539 vlc_bool_t b_uline, int i_karaoke_bgcolor,
1540 line_desc_t *p_line, uint32_t *psz_unicode,
1541 int *pi_pen_x, int i_pen_y, int *pi_start,
1542 FT_Vector *p_result )
1547 vlc_bool_t b_first_on_line = VLC_TRUE;
1550 int i_pen_x_start = *pi_pen_x;
1552 uint32_t *psz_unicode_start = psz_unicode;
1554 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1556 /* Account for part of line already in position */
1557 for( i=0; i<*pi_start; i++ )
1561 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1562 ft_glyph_bbox_pixels, &glyph_size );
1564 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1565 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1566 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1567 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1573 b_first_on_line = VLC_FALSE;
1575 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1581 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1582 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1586 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1587 ft_kerning_default, &delta );
1588 *pi_pen_x += delta.x >> 6;
1590 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1591 p_line->p_glyph_pos[ i ].y = i_pen_y;
1593 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1597 "unable to render text FT_Load_Glyph returned %d", i_error );
1598 p_line->pp_glyphs[ i ] = NULL;
1599 return VLC_EGENERIC;
1601 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1605 "unable to render text FT_Get_Glyph returned %d", i_error );
1606 p_line->pp_glyphs[ i ] = NULL;
1607 return VLC_EGENERIC;
1609 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1610 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1613 FT_Done_Glyph( tmp_glyph );
1618 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1619 p_face->size->metrics.y_scale));
1620 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1621 p_face->size->metrics.y_scale));
1623 p_line->pi_underline_offset[ i ] =
1624 ( aOffset < 0 ) ? -aOffset : aOffset;
1625 p_line->pi_underline_thickness[ i ] =
1626 ( aSize < 0 ) ? -aSize : aSize;
1628 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1629 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1630 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1631 p_line->p_fg_bg_ratio[ i ] = 0x00;
1633 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1634 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1635 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1637 while( --i > *pi_start )
1639 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1642 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1646 if( psz_unicode == psz_unicode_start )
1648 if( b_first_on_line )
1650 msg_Warn( p_filter, "unbreakable string" );
1651 p_line->pp_glyphs[ i ] = NULL;
1652 return VLC_EGENERIC;
1654 *pi_pen_x = i_pen_x_start;
1656 p_line->i_width = line.xMax;
1657 p_line->i_height = __MAX( p_line->i_height,
1658 p_face->size->metrics.height >> 6 );
1659 p_line->pp_glyphs[ i ] = NULL;
1661 p_result->x = __MAX( p_result->x, line.xMax );
1662 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1663 i_yMax - i_yMin ) );
1670 *psz_unicode = '\n';
1672 psz_unicode = psz_unicode_start;
1673 *pi_pen_x = i_pen_x_start;
1681 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1682 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1684 i_previous = i_glyph_index;
1685 *pi_pen_x += p_face->glyph->advance.x >> 6;
1688 p_line->i_width = line.xMax;
1689 p_line->i_height = __MAX( p_line->i_height,
1690 p_face->size->metrics.height >> 6 );
1691 p_line->pp_glyphs[ i ] = NULL;
1693 p_result->x = __MAX( p_result->x, line.xMax );
1694 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1695 line.yMax - line.yMin ) );
1699 /* Get rid of any text processed - if necessary repositioning
1700 * at the start of a new line of text
1704 *psz_unicode_start = '\0';
1709 for( i=0; psz_unicode[ i ]; i++ )
1710 psz_unicode_start[ i ] = psz_unicode[ i ];
1711 psz_unicode_start[ i ] = '\0';
1717 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1718 font_stack_t **p_fonts, int i_scale )
1721 char *psz_fontname = NULL;
1722 uint32_t i_font_color = 0xffffff;
1723 int i_font_alpha = 0;
1724 uint32_t i_karaoke_bg_color = 0x00ffffff;
1725 int i_font_size = 24;
1727 /* Default all attributes to the top font in the stack -- in case not
1728 * all attributes are specified in the sub-font
1730 if( VLC_SUCCESS == PeekFont( p_fonts,
1734 &i_karaoke_bg_color ))
1736 psz_fontname = strdup( psz_fontname );
1737 i_font_size = i_font_size * 1000 / i_scale;
1739 i_font_alpha = (i_font_color >> 24) & 0xff;
1740 i_font_color &= 0x00ffffff;
1742 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1744 char *psz_name = xml_ReaderName( p_xml_reader );
1745 char *psz_value = xml_ReaderValue( p_xml_reader );
1747 if( psz_name && psz_value )
1749 if( !strcasecmp( "face", psz_name ) )
1751 if( psz_fontname ) free( psz_fontname );
1752 psz_fontname = strdup( psz_value );
1754 else if( !strcasecmp( "size", psz_name ) )
1756 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1758 int i_value = atoi( psz_value );
1760 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1761 i_font_size += ( i_value * i_font_size ) / 10;
1762 else if( i_value < -5 )
1763 i_font_size = - i_value;
1764 else if( i_value > 5 )
1765 i_font_size = i_value;
1768 i_font_size = atoi( psz_value );
1770 else if( !strcasecmp( "color", psz_name ) &&
1771 ( psz_value[0] == '#' ) )
1773 i_font_color = strtol( psz_value + 1, NULL, 16 );
1774 i_font_color &= 0x00ffffff;
1776 else if( !strcasecmp( "alpha", psz_name ) &&
1777 ( psz_value[0] == '#' ) )
1779 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1780 i_font_alpha &= 0xff;
1786 rv = PushFont( p_fonts,
1788 i_font_size * i_scale / 1000,
1789 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1790 i_karaoke_bg_color );
1792 free( psz_fontname );
1797 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1798 uint32_t **psz_text_out, uint32_t *pi_runs,
1799 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1800 ft_style_t *p_style )
1802 uint32_t i_string_length = 0;
1804 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1805 *psz_text_out += i_string_length;
1807 if( ppp_styles && ppi_run_lengths )
1813 *ppp_styles = (ft_style_t **)
1814 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1816 else if( *pi_runs == 1 )
1818 *ppp_styles = (ft_style_t **)
1819 malloc( *pi_runs * sizeof( ft_style_t * ) );
1822 /* We have just malloc'ed this memory successfully -
1823 * *pi_runs HAS to be within the memory area of *ppp_styles */
1826 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1830 if( *ppi_run_lengths )
1832 *ppi_run_lengths = (uint32_t *)
1833 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1835 else if( *pi_runs == 1 )
1837 *ppi_run_lengths = (uint32_t *)
1838 malloc( *pi_runs * sizeof( uint32_t ) );
1841 /* same remarks here */
1842 if( *ppi_run_lengths )
1844 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1847 /* If we couldn't use the p_style argument due to memory allocation
1848 * problems above, release it here.
1850 if( p_style ) DeleteStyle( p_style );
1853 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1854 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1856 /* Karaoke tags _PRECEDE_ the text they specify a duration
1857 * for, therefore we are working out the length for the
1858 * previous tag, and first time through we have nothing
1860 if( pi_k_run_lengths )
1865 /* Work out how many characters are presently in the string
1867 for( i = 0; i < i_runs; i++ )
1868 i_chars += pi_run_lengths[ i ];
1870 /* Subtract away those we've already allocated to other
1873 for( i = 0; i < i_k_runs; i++ )
1874 i_chars -= pi_k_run_lengths[ i ];
1876 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1880 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1881 uint32_t **ppi_k_run_lengths,
1882 uint32_t **ppi_k_durations )
1884 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1886 char *psz_name = xml_ReaderName( p_xml_reader );
1887 char *psz_value = xml_ReaderValue( p_xml_reader );
1889 if( psz_name && psz_value &&
1890 !strcasecmp( "t", psz_name ) )
1892 if( ppi_k_durations && ppi_k_run_lengths )
1896 if( *ppi_k_durations )
1898 *ppi_k_durations = (uint32_t *)
1899 realloc( *ppi_k_durations,
1900 *pi_k_runs * sizeof( uint32_t ) );
1902 else if( *pi_k_runs == 1 )
1904 *ppi_k_durations = (uint32_t *)
1905 malloc( *pi_k_runs * sizeof( uint32_t ) );
1908 if( *ppi_k_run_lengths )
1910 *ppi_k_run_lengths = (uint32_t *)
1911 realloc( *ppi_k_run_lengths,
1912 *pi_k_runs * sizeof( uint32_t ) );
1914 else if( *pi_k_runs == 1 )
1916 *ppi_k_run_lengths = (uint32_t *)
1917 malloc( *pi_k_runs * sizeof( uint32_t ) );
1919 if( *ppi_k_durations )
1920 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1922 if( *ppi_k_run_lengths )
1923 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1926 if( psz_name ) free( psz_name );
1927 if( psz_value ) free( psz_value );
1931 static int ProcessNodes( filter_t *p_filter,
1932 xml_reader_t *p_xml_reader,
1933 text_style_t *p_font_style,
1938 uint32_t **ppi_run_lengths,
1939 ft_style_t ***ppp_styles,
1941 vlc_bool_t b_karaoke,
1942 uint32_t *pi_k_runs,
1943 uint32_t **ppi_k_run_lengths,
1944 uint32_t **ppi_k_durations )
1946 int rv = VLC_SUCCESS;
1947 filter_sys_t *p_sys = p_filter->p_sys;
1948 uint32_t *psz_text_orig = psz_text;
1949 font_stack_t *p_fonts = NULL;
1953 char *psz_node = NULL;
1955 vlc_bool_t b_italic = VLC_FALSE;
1956 vlc_bool_t b_bold = VLC_FALSE;
1957 vlc_bool_t b_uline = VLC_FALSE;
1959 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1960 i_scale = val.i_int;
1964 rv = PushFont( &p_fonts,
1965 p_font_style->psz_fontname,
1966 p_font_style->i_font_size * i_scale / 1000,
1967 (p_font_style->i_font_color & 0xffffff) |
1968 ((p_font_style->i_font_alpha & 0xff) << 24),
1969 (p_font_style->i_karaoke_background_color & 0xffffff) |
1970 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1972 if( p_font_style->i_style_flags & STYLE_BOLD )
1974 if( p_font_style->i_style_flags & STYLE_ITALIC )
1975 b_italic = VLC_TRUE;
1976 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1981 rv = PushFont( &p_fonts,
1987 if( rv != VLC_SUCCESS )
1990 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1992 switch ( xml_ReaderNodeType( p_xml_reader ) )
1994 case XML_READER_NONE:
1996 case XML_READER_ENDELEM:
1997 psz_node = xml_ReaderName( p_xml_reader );
2001 if( !strcasecmp( "font", psz_node ) )
2002 PopFont( &p_fonts );
2003 else if( !strcasecmp( "b", psz_node ) )
2005 else if( !strcasecmp( "i", psz_node ) )
2006 b_italic = VLC_FALSE;
2007 else if( !strcasecmp( "u", psz_node ) )
2008 b_uline = VLC_FALSE;
2013 case XML_READER_STARTELEM:
2014 psz_node = xml_ReaderName( p_xml_reader );
2017 if( !strcasecmp( "font", psz_node ) )
2018 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2019 else if( !strcasecmp( "b", psz_node ) )
2021 else if( !strcasecmp( "i", psz_node ) )
2022 b_italic = VLC_TRUE;
2023 else if( !strcasecmp( "u", psz_node ) )
2025 else if( !strcasecmp( "br", psz_node ) )
2027 SetupLine( p_filter, "\n", &psz_text,
2028 pi_runs, ppi_run_lengths, ppp_styles,
2029 GetStyleFromFontStack( p_sys,
2035 else if( !strcasecmp( "k", psz_node ) )
2037 /* Only valid in karaoke */
2040 if( *pi_k_runs > 0 )
2042 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2043 *pi_k_runs, *ppi_k_run_lengths );
2045 SetupKaraoke( p_xml_reader, pi_k_runs,
2046 ppi_k_run_lengths, ppi_k_durations );
2053 case XML_READER_TEXT:
2054 psz_node = xml_ReaderValue( p_xml_reader );
2057 /* Turn any multiple-whitespaces into single spaces */
2058 char *s = strpbrk( psz_node, "\t\r\n " );
2061 int i_whitespace = strspn( s, "\t\r\n " );
2063 if( i_whitespace > 1 )
2066 strlen( s ) - i_whitespace + 1 );
2069 s = strpbrk( s, "\t\r\n " );
2071 SetupLine( p_filter, psz_node, &psz_text,
2072 pi_runs, ppi_run_lengths, ppp_styles,
2073 GetStyleFromFontStack( p_sys,
2082 if( rv != VLC_SUCCESS )
2084 psz_text = psz_text_orig;
2090 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2091 *pi_k_runs, *ppi_k_run_lengths );
2094 *pi_len = psz_text - psz_text_orig;
2096 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2101 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2105 for( k=0; k < p_sys->i_font_attachments; k++ )
2107 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2109 FT_Face p_face = NULL;
2111 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2119 vlc_bool_t match = !strcasecmp( p_face->family_name,
2120 p_style->psz_fontname );
2122 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2123 match = match && p_style->b_bold;
2125 match = match && !p_style->b_bold;
2127 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2128 match = match && p_style->b_italic;
2130 match = match && !p_style->b_italic;
2138 FT_Done_Face( p_face );
2143 return VLC_EGENERIC;
2146 static int ProcessLines( filter_t *p_filter,
2151 uint32_t *pi_run_lengths,
2152 ft_style_t **pp_styles,
2153 line_desc_t **pp_lines,
2155 FT_Vector *p_result,
2157 vlc_bool_t b_karaoke,
2159 uint32_t *pi_k_run_lengths,
2160 uint32_t *pi_k_durations )
2162 filter_sys_t *p_sys = p_filter->p_sys;
2163 ft_style_t **pp_char_styles;
2164 int *p_new_positions = NULL;
2165 int8_t *p_levels = NULL;
2166 uint8_t *pi_karaoke_bar = NULL;
2170 /* Assign each character in the text string its style explicitly, so that
2171 * after the characters have been shuffled around by Fribidi, we can re-apply
2172 * the styles, and to simplify the calculation of runs within a line.
2174 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2175 if( !pp_char_styles )
2180 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2181 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2182 * we just won't be able to display the progress bar; at least we'll
2188 for( j = 0; j < i_runs; j++ )
2189 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2190 pp_char_styles[ i++ ] = pp_styles[ j ];
2192 #if defined(HAVE_FRIBIDI)
2194 ft_style_t **pp_char_styles_new;
2195 int *p_old_positions;
2196 uint32_t *p_fribidi_string;
2197 int start_pos, pos = 0;
2199 pp_char_styles_new = (ft_style_t **)
2200 malloc( i_len * sizeof( ft_style_t * ));
2202 p_fribidi_string = (uint32_t *)
2203 malloc( (i_len + 1) * sizeof(uint32_t) );
2204 p_old_positions = (int *)
2205 malloc( (i_len + 1) * sizeof( int ) );
2206 p_new_positions = (int *)
2207 malloc( (i_len + 1) * sizeof( int ) );
2208 p_levels = (int8_t *)
2209 malloc( (i_len + 1) * sizeof( int8_t ) );
2211 if( ! pp_char_styles_new ||
2212 ! p_fribidi_string ||
2213 ! p_old_positions ||
2214 ! p_new_positions ||
2217 msg_Err( p_filter, "out of memory" );
2218 if( p_levels ) free( p_levels );
2219 if( p_old_positions ) free( p_old_positions );
2220 if( p_new_positions ) free( p_new_positions );
2221 if( p_fribidi_string ) free( p_fribidi_string );
2222 if( pp_char_styles_new ) free( pp_char_styles_new );
2223 if( pi_karaoke_bar ) free( pi_karaoke_bar );
2225 free( pp_char_styles );
2229 /* Do bidi conversion line-by-line */
2232 while(pos < i_len) {
2233 if (psz_text[pos] != '\n')
2235 p_fribidi_string[pos] = psz_text[pos];
2236 pp_char_styles_new[pos] = pp_char_styles[pos];
2237 p_new_positions[pos] = pos;
2242 while(pos < i_len) {
2243 if (psz_text[pos] == '\n')
2247 if (pos > start_pos)
2249 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2250 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2251 pos - start_pos, &base_dir,
2252 (FriBidiChar*)p_fribidi_string + start_pos,
2253 p_new_positions + start_pos,
2255 p_levels + start_pos );
2256 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2258 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2259 p_old_positions[ j - start_pos ] ];
2260 p_new_positions[ j ] += start_pos;
2264 free( p_old_positions );
2265 free( pp_char_styles );
2266 pp_char_styles = pp_char_styles_new;
2267 psz_text = p_fribidi_string;
2268 p_fribidi_string[ i_len ] = 0;
2271 /* Work out the karaoke */
2272 if( pi_karaoke_bar )
2274 int64_t i_last_duration = 0;
2275 int64_t i_duration = 0;
2276 int64_t i_start_pos = 0;
2277 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2279 for( k = 0; k< i_k_runs; k++ )
2281 double fraction = 0.0;
2283 i_duration += pi_k_durations[ k ];
2285 if( i_duration < i_elapsed )
2287 /* Completely finished this run-length -
2288 * let it render normally */
2292 else if( i_elapsed < i_last_duration )
2294 /* Haven't got up to this segment yet -
2295 * render it completely in karaoke BG mode */
2301 /* Partway through this run */
2303 fraction = (double)(i_elapsed - i_last_duration) /
2304 (double)pi_k_durations[ k ];
2306 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2308 double shade = pi_k_run_lengths[ k ] * fraction;
2310 if( p_new_positions )
2311 j = p_new_positions[ i_start_pos + i ];
2313 j = i_start_pos + i;
2315 if( i < (uint32_t)shade )
2316 pi_karaoke_bar[ j ] = 0xff;
2317 else if( (double)i > shade )
2318 pi_karaoke_bar[ j ] = 0x00;
2321 shade -= (int)shade;
2322 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2323 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2327 i_last_duration = i_duration;
2328 i_start_pos += pi_k_run_lengths[ k ];
2331 if( p_levels ) free( p_levels );
2332 if( p_new_positions ) free( p_new_positions );
2334 FT_Vector tmp_result;
2336 line_desc_t *p_line = NULL;
2337 line_desc_t *p_prev = NULL;
2343 p_result->x = p_result->y = 0;
2344 tmp_result.x = tmp_result.y = 0;
2347 for( k = 0; k <= (uint32_t) i_len; k++ )
2349 if( ( k == (uint32_t) i_len ) ||
2351 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2353 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2355 /* End of the current style run */
2356 FT_Face p_face = NULL;
2359 /* Look for a match amongst our attachments first */
2360 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2362 if( ! p_face && p_sys->b_fontconfig_ok )
2366 vlc_mutex_lock( &p_sys->fontconfig_lock );
2368 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2369 p_style->psz_fontname,
2374 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2376 if( psz_fontfile && ! *psz_fontfile )
2378 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2379 " so using default font", p_style->psz_fontname,
2380 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2381 (p_style->b_bold ? "(Bold)" :
2382 (p_style->b_italic ? "(Italic)" : ""))) );
2383 free( psz_fontfile );
2384 psz_fontfile = NULL;
2389 if( FT_New_Face( p_sys->p_library,
2390 psz_fontfile, i_idx, &p_face ) )
2392 free( psz_fontfile );
2393 free( pp_char_styles );
2394 #if defined(HAVE_FRIBIDI)
2397 if( pi_karaoke_bar )
2398 free( pi_karaoke_bar );
2399 return VLC_EGENERIC;
2401 free( psz_fontfile );
2405 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2407 /* We've loaded a font face which is unhelpful for actually
2408 * rendering text - fallback to the default one.
2410 FT_Done_Face( p_face );
2414 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2415 ft_encoding_unicode ) ||
2416 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2417 p_style->i_font_size ) )
2419 if( p_face ) FT_Done_Face( p_face );
2420 free( pp_char_styles );
2421 #if defined(HAVE_FRIBIDI)
2424 if( pi_karaoke_bar )
2425 free( pi_karaoke_bar );
2426 return VLC_EGENERIC;
2428 p_sys->i_use_kerning =
2429 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2432 uint32_t *psz_unicode = (uint32_t *)
2433 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2436 msg_Err( p_filter, "out of memory" );
2437 if( p_face ) FT_Done_Face( p_face );
2438 free( pp_char_styles );
2439 free( psz_unicode );
2440 #if defined(HAVE_FRIBIDI)
2443 if( pi_karaoke_bar )
2444 free( pi_karaoke_bar );
2447 memcpy( psz_unicode, psz_text + i_prev,
2448 sizeof( uint32_t ) * ( k - i_prev ) );
2449 psz_unicode[ k - i_prev ] = 0;
2450 while( *psz_unicode )
2454 if( !(p_line = NewLine( i_len - i_prev)) )
2456 msg_Err( p_filter, "out of memory" );
2457 if( p_face ) FT_Done_Face( p_face );
2458 free( pp_char_styles );
2459 free( psz_unicode );
2460 #if defined(HAVE_FRIBIDI)
2463 if( pi_karaoke_bar )
2464 free( pi_karaoke_bar );
2467 /* New Color mode only works in YUVA rendering mode --
2468 * (RGB mode has palette constraints on it). We therefore
2469 * need to populate the legacy colour fields also.
2471 p_line->b_new_color_mode = VLC_TRUE;
2472 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2473 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2474 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2475 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2476 p_line->p_next = NULL;
2478 i_pen_y += tmp_result.y;
2482 if( p_prev ) p_prev->p_next = p_line;
2483 else *pp_lines = p_line;
2486 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2487 p_style->i_font_color, p_style->b_underline,
2488 p_style->i_karaoke_bg_color,
2489 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2490 &tmp_result ) != VLC_SUCCESS )
2492 if( p_face ) FT_Done_Face( p_face );
2493 free( pp_char_styles );
2494 free( psz_unicode );
2495 #if defined(HAVE_FRIBIDI)
2498 if( pi_karaoke_bar )
2499 free( pi_karaoke_bar );
2500 return VLC_EGENERIC;
2505 p_result->x = __MAX( p_result->x, tmp_result.x );
2506 p_result->y += tmp_result.y;
2512 free( psz_unicode );
2513 if( p_face ) FT_Done_Face( p_face );
2517 free( pp_char_styles );
2518 #if defined(HAVE_FRIBIDI)
2523 p_result->x = __MAX( p_result->x, tmp_result.x );
2524 p_result->y += tmp_result.y;
2527 if( pi_karaoke_bar )
2530 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2532 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2534 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2538 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2540 /* 100% BG colour will render faster if we
2541 * instead make it 100% FG colour, so leave
2542 * the ratio alone and copy the value across
2544 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2548 if( pi_karaoke_bar[ i ] & 0x80 )
2550 /* Swap Left and Right sides over for Right aligned
2551 * language text (eg. Arabic, Hebrew)
2553 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2555 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2556 p_line->p_bg_rgb[ k ] = i_tmp;
2558 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2561 /* Jump over the '\n' at the line-end */
2564 free( pi_karaoke_bar );
2570 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2571 subpicture_region_t *p_region_in )
2573 int rv = VLC_SUCCESS;
2574 stream_t *p_sub = NULL;
2575 xml_t *p_xml = NULL;
2576 xml_reader_t *p_xml_reader = NULL;
2578 if( !p_region_in || !p_region_in->psz_html )
2579 return VLC_EGENERIC;
2581 /* Reset the default fontsize in case screen metrics have changed */
2582 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2584 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2585 (uint8_t *) p_region_in->psz_html,
2586 strlen( p_region_in->psz_html ),
2590 p_xml = xml_Create( p_filter );
2593 vlc_bool_t b_karaoke = VLC_FALSE;
2595 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2598 /* Look for Root Node */
2599 if( xml_ReaderRead( p_xml_reader ) == 1 )
2601 char *psz_node = xml_ReaderName( p_xml_reader );
2603 if( !strcasecmp( "karaoke", psz_node ) )
2605 /* We're going to have to render the text a number
2606 * of times to show the progress marker on the text.
2608 var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2609 b_karaoke = VLC_TRUE;
2611 else if( !strcasecmp( "text", psz_node ) )
2613 b_karaoke = VLC_FALSE;
2617 /* Only text and karaoke tags are supported */
2618 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2619 xml_ReaderDelete( p_xml, p_xml_reader );
2620 p_xml_reader = NULL;
2632 uint32_t i_runs = 0;
2633 uint32_t i_k_runs = 0;
2634 uint32_t *pi_run_lengths = NULL;
2635 uint32_t *pi_k_run_lengths = NULL;
2636 uint32_t *pi_k_durations = NULL;
2637 ft_style_t **pp_styles = NULL;
2639 line_desc_t *p_lines = NULL;
2641 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2642 sizeof( uint32_t ) );
2647 rv = ProcessNodes( p_filter, p_xml_reader,
2648 p_region_in->p_style, psz_text, &i_len,
2649 &i_runs, &pi_run_lengths, &pp_styles,
2650 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2653 p_region_out->i_x = p_region_in->i_x;
2654 p_region_out->i_y = p_region_in->i_y;
2656 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2658 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2659 pi_run_lengths, pp_styles, &p_lines, &result,
2660 b_karaoke, i_k_runs, pi_k_run_lengths,
2664 for( k=0; k<i_runs; k++)
2665 DeleteStyle( pp_styles[k] );
2667 free( pi_run_lengths );
2670 /* Don't attempt to render text that couldn't be layed out
2673 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2675 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2677 Render( p_filter, p_region_out, p_lines,
2678 result.x, result.y );
2682 RenderYUVA( p_filter, p_region_out, p_lines,
2683 result.x, result.y );
2687 FreeLines( p_lines );
2689 xml_ReaderDelete( p_xml, p_xml_reader );
2691 xml_Delete( p_xml );
2693 stream_Delete( p_sub );
2699 static char* FontConfig_Select( FcConfig* priv, const char* family,
2700 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2703 FcPattern *pat, *p_pat;
2707 pat = FcPatternCreate();
2708 if (!pat) return NULL;
2710 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2711 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2712 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2713 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2715 FcDefaultSubstitute( pat );
2717 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2719 FcPatternDestroy( pat );
2723 p_pat = FcFontMatch( priv, pat, &result );
2724 FcPatternDestroy( pat );
2725 if( !p_pat ) return NULL;
2727 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2728 || ( val_b != FcTrue ) )
2730 FcPatternDestroy( p_pat );
2733 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2738 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2740 FcPatternDestroy( p_pat );
2745 if( strcasecmp((const char*)val_s, family ) != 0 )
2746 msg_Warn( p_filter, "fontconfig: selected font family is not"
2747 "the requested one: '%s' != '%s'\n",
2748 (const char*)val_s, family );
2751 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2753 FcPatternDestroy( p_pat );
2757 FcPatternDestroy( p_pat );
2758 return strdup( (const char*)val_s );
2762 static void FreeLine( line_desc_t *p_line )
2765 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2767 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2769 free( p_line->pp_glyphs );
2770 free( p_line->p_glyph_pos );
2771 free( p_line->p_fg_rgb );
2772 free( p_line->p_bg_rgb );
2773 free( p_line->p_fg_bg_ratio );
2774 free( p_line->pi_underline_offset );
2775 free( p_line->pi_underline_thickness );
2779 static void FreeLines( line_desc_t *p_lines )
2781 line_desc_t *p_line, *p_next;
2783 if( !p_lines ) return;
2785 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2787 p_next = p_line->p_next;
2792 static line_desc_t *NewLine( int i_count )
2794 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2796 if( !p_line ) return NULL;
2797 p_line->i_height = 0;
2798 p_line->i_width = 0;
2799 p_line->p_next = NULL;
2801 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2802 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2803 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2804 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2805 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2806 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2807 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2808 if( ( p_line->pp_glyphs == NULL ) ||
2809 ( p_line->p_glyph_pos == NULL ) ||
2810 ( p_line->p_fg_rgb == NULL ) ||
2811 ( p_line->p_bg_rgb == NULL ) ||
2812 ( p_line->p_fg_bg_ratio == NULL ) ||
2813 ( p_line->pi_underline_offset == NULL ) ||
2814 ( p_line->pi_underline_thickness == NULL ) )
2816 if( p_line->pi_underline_thickness )
2817 free( p_line->pi_underline_thickness );
2818 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2819 if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2820 if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2821 if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2822 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2823 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2827 p_line->pp_glyphs[0] = NULL;
2828 p_line->b_new_color_mode = VLC_FALSE;
2833 static int GetFontSize( filter_t *p_filter )
2835 filter_sys_t *p_sys = p_filter->p_sys;
2839 if( p_sys->i_default_font_size )
2841 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2842 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2844 i_size = p_sys->i_default_font_size;
2848 var_Get( p_filter, "freetype-rel-fontsize", &val );
2849 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2850 p_filter->p_sys->i_display_height =
2851 p_filter->fmt_out.video.i_height;
2855 msg_Warn( p_filter, "invalid fontsize, using 12" );
2856 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2857 i_size = 12 * val.i_int / 1000;
2864 static int SetFontSize( filter_t *p_filter, int i_size )
2866 filter_sys_t *p_sys = p_filter->p_sys;
2870 i_size = GetFontSize( p_filter );
2872 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2875 p_sys->i_font_size = i_size;
2877 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2879 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2880 return VLC_EGENERIC;
2886 static void YUVFromRGB( uint32_t i_argb,
2887 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2889 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2890 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2891 int i_blue = ( i_argb & 0x000000ff );
2893 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2894 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2895 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2896 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2897 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2898 -585 * i_blue + 4096 + 1048576) >> 13, 240);