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 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
42 #include <vlc_input.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
52 #define FT_MulFix(v, s) (((v)*(s))>>16)
55 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
56 #define FC_DEFAULT_FONT "Lucida Grande"
57 #elif defined( SYS_BEOS )
58 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
59 #define FC_DEFAULT_FONT "Swiss"
60 #elif defined( WIN32 )
61 #define DEFAULT_FONT "" /* Default font found at run-time */
62 #define FC_DEFAULT_FONT "Arial"
64 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
65 #define FC_DEFAULT_FONT "Serif Bold"
68 #if defined(HAVE_FRIBIDI)
69 #include <fribidi/fribidi.h>
72 #ifdef HAVE_FONTCONFIG
73 #include <fontconfig/fontconfig.h>
78 /*****************************************************************************
80 *****************************************************************************/
81 static int Create ( vlc_object_t * );
82 static void Destroy( vlc_object_t * );
84 #define FONT_TEXT N_("Font")
85 #define FONT_LONGTEXT N_("Filename for the font you want to use")
86 #define FONTSIZE_TEXT N_("Font size in pixels")
87 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
88 "that will be rendered on the video. " \
89 "If set to something different than 0 this option will override the " \
90 "relative font size." )
91 #define OPACITY_TEXT N_("Opacity")
92 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
93 "text that will be rendered on the video. 0 = transparent, " \
94 "255 = totally opaque. " )
95 #define COLOR_TEXT N_("Text default color")
96 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
97 "the video. This must be an hexadecimal (like HTML colors). The first two "\
98 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
99 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
100 #define FONTSIZER_TEXT N_("Relative font size")
101 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
102 "fonts that will be rendered on the video. If absolute font size is set, "\
103 "relative size will be overriden." )
105 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
106 static const char *const ppsz_sizes_text[] = {
107 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
108 #define YUVP_TEXT N_("Use YUVP renderer")
109 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
110 "This option is only needed if you want to encode into DVB subtitles" )
111 #define EFFECT_TEXT N_("Font Effect")
112 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
113 "text to improve its readability." )
115 #define EFFECT_BACKGROUND 1
116 #define EFFECT_OUTLINE 2
117 #define EFFECT_OUTLINE_FAT 3
119 static int const pi_effects[] = { 1, 2, 3 };
120 static const char *const ppsz_effects_text[] = {
121 N_("Background"),N_("Outline"), N_("Fat Outline") };
122 static const int pi_color_values[] = {
123 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
124 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
125 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
127 static const char *const ppsz_color_descriptions[] = {
128 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
129 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
130 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
133 set_shortname( N_("Text renderer"));
134 set_description( N_("Freetype2 font renderer") );
135 set_category( CAT_VIDEO );
136 set_subcategory( SUBCAT_VIDEO_SUBPIC );
138 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
141 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
142 FONTSIZE_LONGTEXT, true );
144 /* opacity valid on 0..255, with default 255 = fully opaque */
145 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
146 OPACITY_TEXT, OPACITY_LONGTEXT, true );
148 /* hook to the color values list, with default 0x00ffffff = white */
149 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
150 COLOR_LONGTEXT, false );
151 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
153 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
154 FONTSIZER_LONGTEXT, false );
155 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
156 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
157 EFFECT_LONGTEXT, false );
158 change_integer_list( pi_effects, ppsz_effects_text, 0 );
160 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
161 YUVP_LONGTEXT, true );
162 set_capability( "text renderer", 100 );
163 add_shortcut( "text" );
164 set_callbacks( Create, Destroy );
169 /*****************************************************************************
171 *****************************************************************************/
173 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
174 static int RenderText( filter_t *, subpicture_region_t *,
175 subpicture_region_t * );
176 #ifdef HAVE_FONTCONFIG
177 static int RenderHtml( filter_t *, subpicture_region_t *,
178 subpicture_region_t * );
179 static char *FontConfig_Select( FcConfig *, const char *,
184 static int LoadFontsFromAttachments( filter_t *p_filter );
186 static int GetFontSize( filter_t *p_filter );
187 static int SetFontSize( filter_t *, int );
188 static void YUVFromRGB( uint32_t i_argb,
189 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
191 typedef struct line_desc_t line_desc_t;
194 /** NULL-terminated list of glyphs making the string */
195 FT_BitmapGlyph *pp_glyphs;
196 /** list of relative positions for the glyphs */
197 FT_Vector *p_glyph_pos;
198 /** list of RGB information for styled text
199 * -- if the rendering mode supports it (RenderYUVA) and
200 * b_new_color_mode is set, then it becomes possible to
201 * have multicoloured text within the subtitles. */
204 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
205 bool b_new_color_mode;
206 /** underline information -- only supplied if text should be underlined */
207 uint16_t *pi_underline_offset;
208 uint16_t *pi_underline_thickness;
212 int i_red, i_green, i_blue;
217 static line_desc_t *NewLine( int );
219 typedef struct font_stack_t font_stack_t;
224 uint32_t i_color; /* ARGB */
225 uint32_t i_karaoke_bg_color; /* ARGB */
227 font_stack_t *p_next;
233 uint32_t i_font_color; /* ARGB */
234 uint32_t i_karaoke_bg_color; /* ARGB */
241 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
242 static void FreeLines( line_desc_t * );
243 static void FreeLine( line_desc_t * );
245 #ifdef HAVE_FONTCONFIG
246 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock );
247 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
248 static void FontBuilderThread( vlc_object_t *p_this);
249 static void FontBuilderDestructor( vlc_object_t *p_this );
250 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
254 /*****************************************************************************
255 * filter_sys_t: freetype local data
256 *****************************************************************************
257 * This structure is part of the video output thread descriptor.
258 * It describes the freetype specific properties of an output thread.
259 *****************************************************************************/
262 FT_Library p_library; /* handle to library */
263 FT_Face p_face; /* handle to face object */
265 uint8_t i_font_opacity;
270 int i_default_font_size;
271 int i_display_height;
272 #ifdef HAVE_FONTCONFIG
273 vlc_mutex_t *p_fontconfig_lock;
274 bool b_fontconfig_ok;
275 FcConfig *p_fontconfig;
278 input_attachment_t **pp_font_attachments;
279 int i_font_attachments;
281 vlc_object_t *p_fontbuilder;
284 /*****************************************************************************
285 * Create: allocates osd-text video thread output method
286 *****************************************************************************
287 * This function allocates and initializes a Clone vout method.
288 *****************************************************************************/
289 static int Create( vlc_object_t *p_this )
291 filter_t *p_filter = (filter_t *)p_this;
293 char *psz_fontfile = NULL;
297 /* Allocate structure */
298 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
302 p_sys->p_library = 0;
303 p_sys->i_font_size = 0;
304 p_sys->i_display_height = 0;
306 var_Create( p_filter, "freetype-font",
307 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
308 var_Create( p_filter, "freetype-fontsize",
309 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
310 var_Create( p_filter, "freetype-rel-fontsize",
311 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
312 var_Create( p_filter, "freetype-opacity",
313 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
314 var_Create( p_filter, "freetype-effect",
315 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
316 var_Get( p_filter, "freetype-opacity", &val );
317 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
318 var_Create( p_filter, "freetype-color",
319 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
320 var_Get( p_filter, "freetype-color", &val );
321 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
322 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
324 /* Look what method was requested */
325 var_Get( p_filter, "freetype-font", &val );
326 psz_fontfile = val.psz_string;
327 if( !psz_fontfile || !*psz_fontfile )
329 free( psz_fontfile );
330 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
334 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
335 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
336 #elif defined(__APPLE__)
337 strcpy( psz_fontfile, DEFAULT_FONT );
339 msg_Err( p_filter, "user didn't specify a font" );
344 i_error = FT_Init_FreeType( &p_sys->p_library );
347 msg_Err( p_filter, "couldn't initialize freetype" );
350 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
352 if( i_error == FT_Err_Unknown_File_Format )
354 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
359 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
363 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
366 msg_Err( p_filter, "font has no unicode translation table" );
370 #ifdef HAVE_FONTCONFIG
371 p_sys->b_fontconfig_ok = false;
372 p_sys->p_fontconfig = NULL;
373 p_sys->p_fontbuilder = FontBuilderAttach( p_filter, &p_sys->p_fontconfig_lock );
376 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
378 var_Get( p_filter, "freetype-fontsize", &val );
379 p_sys->i_default_font_size = val.i_int;
380 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
382 free( psz_fontfile );
384 p_sys->pp_font_attachments = NULL;
385 p_sys->i_font_attachments = 0;
387 p_filter->pf_render_text = RenderText;
388 #ifdef HAVE_FONTCONFIG
389 p_filter->pf_render_html = RenderHtml;
391 p_filter->pf_render_html = NULL;
394 LoadFontsFromAttachments( p_filter );
399 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
400 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
401 free( psz_fontfile );
406 /*****************************************************************************
407 * Destroy: destroy Clone video thread output method
408 *****************************************************************************
409 * Clean up all data and library connections
410 *****************************************************************************/
411 static void Destroy( vlc_object_t *p_this )
413 filter_t *p_filter = (filter_t *)p_this;
414 filter_sys_t *p_sys = p_filter->p_sys;
416 if( p_sys->pp_font_attachments )
420 for( k = 0; k < p_sys->i_font_attachments; k++ )
421 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
423 free( p_sys->pp_font_attachments );
426 #ifdef HAVE_FONTCONFIG
427 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
430 /* FcFini asserts calling the subfunction FcCacheFini()
431 * even if no other library functions have been made since FcInit(),
432 * so don't call it. */
434 FT_Done_Face( p_sys->p_face );
435 FT_Done_FreeType( p_sys->p_library );
439 #ifdef HAVE_FONTCONFIG
440 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock )
442 /* Check for an existing Fontbuilder thread */
443 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
444 vlc_object_t *p_fontbuilder =
445 vlc_object_find_name( p_filter->p_libvlc,
446 "fontlist builder", FIND_CHILD );
450 /* Create the FontBuilderThread thread as a child of a top-level
451 * object, so that it can survive the destruction of the
452 * freetype object - the fontlist only needs to be built once,
453 * and calling the fontbuild a second time while the first is
454 * still in progress can cause thread instabilities.
456 * XXX The fontbuilder will be destroy as soon as it is unused.
459 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
460 sizeof(vlc_object_t) );
463 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
464 p_fontbuilder->p_private = NULL;
465 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
467 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
469 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
470 var_SetBool( p_fontbuilder, "build-done", false );
472 if( vlc_thread_create( p_fontbuilder,
475 VLC_THREAD_PRIORITY_LOW,
478 msg_Warn( p_filter, "fontconfig database builder thread can't "
479 "be launched. Font styling support will be limited." );
485 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
486 var_TriggerCallback( p_fontbuilder, "build-done" );
488 vlc_mutex_unlock( p_lock );
490 return p_fontbuilder;
492 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
494 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
497 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
499 /* We wait for the thread on the first FontBuilderDetach */
500 if( vlc_object_alive( p_fontbuilder ) )
502 vlc_object_kill( p_fontbuilder );
503 vlc_thread_join( p_fontbuilder );
505 vlc_object_release( p_fontbuilder );
507 vlc_mutex_unlock( lock );
509 static void FontBuilderThread( vlc_object_t *p_this )
511 FcConfig *p_fontconfig = FcInitLoadConfig();
513 vlc_thread_ready( p_this );
519 //msg_Dbg( p_this, "Building font database..." );
520 msg_Dbg( p_this, "Building font database..." );
522 if(! FcConfigBuildFonts( p_fontconfig ))
524 /* Don't destroy the fontconfig object - we won't be able to do
525 * italics or bold or change the font face, but we will still
526 * be able to do underline and change the font size.
528 msg_Err( p_this, "fontconfig database can't be built. "
529 "Font styling won't be available" );
533 msg_Dbg( p_this, "Finished building font database." );
534 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
536 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
537 p_this->p_private = p_fontconfig;
538 vlc_mutex_unlock( p_lock );
540 var_SetBool( p_this, "build-done", true );
543 static void FontBuilderDestructor( vlc_object_t *p_this )
545 FcConfig *p_fontconfig = p_this->p_private;
548 FcConfigDestroy( p_fontconfig );
550 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
551 vlc_value_t oldval, vlc_value_t newval, void *param )
553 filter_t *p_filter = param;
554 filter_sys_t *p_sys = p_filter->p_sys;
558 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
560 p_sys->b_fontconfig_ok = true;
561 p_sys->p_fontconfig = p_this->p_private;
563 vlc_mutex_unlock( p_lock );
572 /*****************************************************************************
573 * Make any TTF/OTF fonts present in the attachments of the media file
574 * and store them for later use by the FreeType Engine
575 *****************************************************************************/
576 static int LoadFontsFromAttachments( filter_t *p_filter )
578 filter_sys_t *p_sys = p_filter->p_sys;
579 input_thread_t *p_input;
580 input_attachment_t **pp_attachments;
581 int i_attachments_cnt;
583 int rv = VLC_SUCCESS;
585 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
589 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
591 vlc_object_release(p_input);
595 p_sys->i_font_attachments = 0;
596 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
597 if(! p_sys->pp_font_attachments )
600 for( k = 0; k < i_attachments_cnt; k++ )
602 input_attachment_t *p_attach = pp_attachments[k];
604 if( p_sys->pp_font_attachments )
606 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
607 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
608 ( p_attach->i_data > 0 ) &&
609 ( p_attach->p_data != NULL ) )
611 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
615 vlc_input_attachment_Delete( p_attach );
620 vlc_input_attachment_Delete( p_attach );
623 free( pp_attachments );
625 vlc_object_release(p_input);
630 /*****************************************************************************
631 * Render: place string in picture
632 *****************************************************************************
633 * This function merges the previously rendered freetype glyphs into a picture
634 *****************************************************************************/
635 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
636 line_desc_t *p_line, int i_width, int i_height )
638 static uint8_t pi_gamma[16] =
639 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
640 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
644 int i, x, y, i_pitch;
645 uint8_t i_y; /* YUV values, derived from incoming RGB */
647 subpicture_region_t *p_region_tmp;
649 /* Create a new subpicture region */
650 memset( &fmt, 0, sizeof(video_format_t) );
651 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
653 fmt.i_width = fmt.i_visible_width = i_width + 4;
654 fmt.i_height = fmt.i_visible_height = i_height + 4;
655 if( p_region->fmt.i_visible_width > 0 )
656 fmt.i_visible_width = p_region->fmt.i_visible_width;
657 if( p_region->fmt.i_visible_height > 0 )
658 fmt.i_visible_height = p_region->fmt.i_visible_height;
659 fmt.i_x_offset = fmt.i_y_offset = 0;
660 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
663 msg_Err( p_filter, "cannot allocate SPU region" );
667 p_region->fmt = p_region_tmp->fmt;
668 p_region->picture = p_region_tmp->picture;
669 free( p_region_tmp );
671 /* Calculate text color components */
672 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
673 25 * p_line->i_blue + 128) >> 8) + 16;
674 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
675 112 * p_line->i_blue + 128) >> 8) + 128;
676 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
677 18 * p_line->i_blue + 128) >> 8) + 128;
680 fmt.p_palette->i_entries = 16;
681 for( i = 0; i < 8; i++ )
683 fmt.p_palette->palette[i][0] = 0;
684 fmt.p_palette->palette[i][1] = 0x80;
685 fmt.p_palette->palette[i][2] = 0x80;
686 fmt.p_palette->palette[i][3] = pi_gamma[i];
687 fmt.p_palette->palette[i][3] =
688 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
690 for( i = 8; i < fmt.p_palette->i_entries; i++ )
692 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
693 fmt.p_palette->palette[i][1] = i_u;
694 fmt.p_palette->palette[i][2] = i_v;
695 fmt.p_palette->palette[i][3] = pi_gamma[i];
696 fmt.p_palette->palette[i][3] =
697 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
700 p_dst = p_region->picture.Y_PIXELS;
701 i_pitch = p_region->picture.Y_PITCH;
703 /* Initialize the region pixels */
704 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
706 for( ; p_line != NULL; p_line = p_line->p_next )
708 int i_glyph_tmax = 0;
709 int i_bitmap_offset, i_offset, i_align_offset = 0;
710 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
712 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
713 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
716 if( p_line->i_width < i_width )
718 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
720 i_align_offset = i_width - p_line->i_width;
722 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
724 i_align_offset = ( i_width - p_line->i_width ) / 2;
728 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
730 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
732 i_offset = ( p_line->p_glyph_pos[ i ].y +
733 i_glyph_tmax - p_glyph->top + 2 ) *
734 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
737 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
739 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
741 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
743 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
750 /* Outlining (find something better than nearest neighbour filtering ?) */
753 uint8_t *p_dst = p_region->picture.Y_PIXELS;
754 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
755 uint8_t left, current;
757 for( y = 1; y < (int)fmt.i_height - 1; y++ )
759 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
760 p_dst += p_region->picture.Y_PITCH;
763 for( x = 1; x < (int)fmt.i_width - 1; x++ )
766 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
767 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;
771 memset( p_top, 0, fmt.i_width );
777 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
778 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
779 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
780 int i_glyph_tmax, int i_align_offset,
781 uint8_t i_y, uint8_t i_u, uint8_t i_v,
782 subpicture_region_t *p_region)
786 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
788 p_dst_y = p_region->picture.Y_PIXELS;
789 p_dst_u = p_region->picture.U_PIXELS;
790 p_dst_v = p_region->picture.V_PIXELS;
791 p_dst_a = p_region->picture.A_PIXELS;
792 i_pitch = p_region->picture.A_PITCH;
794 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
795 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
797 for( y = 0; y < i_line_thickness; y++ )
799 int i_extra = p_this_glyph->bitmap.width;
803 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
804 (p_this_glyph_pos->x + p_this_glyph->left);
806 for( x = 0; x < i_extra; x++ )
810 /* break the underline around the tails of any glyphs which cross it */
811 for( z = x - i_line_thickness;
812 z < x + i_line_thickness && b_ok;
815 if( p_next_glyph && ( z >= i_extra ) )
817 int i_row = i_line_offset + p_next_glyph->top + y;
819 if( ( p_next_glyph->bitmap.rows > i_row ) &&
820 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
825 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
827 int i_row = i_line_offset + p_this_glyph->top + y;
829 if( ( p_this_glyph->bitmap.rows > i_row ) &&
830 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
839 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
840 p_dst_u[i_offset+x] = i_u;
841 p_dst_v[i_offset+x] = i_v;
842 p_dst_a[i_offset+x] = 255;
849 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
851 uint8_t *p_dst = p_region->picture.A_PIXELS;
852 int i_pitch = p_region->picture.A_PITCH;
855 for( ; p_line != NULL; p_line = p_line->p_next )
857 int i_glyph_tmax=0, i = 0;
858 int i_bitmap_offset, i_offset, i_align_offset = 0;
859 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
861 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
862 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
865 if( p_line->i_width < i_width )
867 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
869 i_align_offset = i_width - p_line->i_width;
871 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
873 i_align_offset = ( i_width - p_line->i_width ) / 2;
877 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
879 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
881 i_offset = ( p_line->p_glyph_pos[ i ].y +
882 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
883 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
884 i_align_offset +xoffset;
886 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
888 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
890 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
891 if( p_dst[i_offset+x] <
892 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
894 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
903 /*****************************************************************************
904 * Render: place string in picture
905 *****************************************************************************
906 * This function merges the previously rendered freetype glyphs into a picture
907 *****************************************************************************/
908 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
909 line_desc_t *p_line, int i_width, int i_height )
911 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
913 int i, x, y, i_pitch, i_alpha;
914 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
915 subpicture_region_t *p_region_tmp;
917 if( i_width == 0 || i_height == 0 )
920 /* Create a new subpicture region */
921 memset( &fmt, 0, sizeof(video_format_t) );
922 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
924 fmt.i_width = fmt.i_visible_width = i_width + 6;
925 fmt.i_height = fmt.i_visible_height = i_height + 6;
926 if( p_region->fmt.i_visible_width > 0 )
927 fmt.i_visible_width = p_region->fmt.i_visible_width;
928 if( p_region->fmt.i_visible_height > 0 )
929 fmt.i_visible_height = p_region->fmt.i_visible_height;
930 fmt.i_x_offset = fmt.i_y_offset = 0;
931 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
934 msg_Err( p_filter, "cannot allocate SPU region" );
938 p_region->fmt = p_region_tmp->fmt;
939 p_region->picture = p_region_tmp->picture;
940 free( p_region_tmp );
942 /* Calculate text color components */
943 YUVFromRGB( (p_line->i_red << 16) |
944 (p_line->i_green << 8) |
947 i_alpha = p_line->i_alpha;
949 p_dst_y = p_region->picture.Y_PIXELS;
950 p_dst_u = p_region->picture.U_PIXELS;
951 p_dst_v = p_region->picture.V_PIXELS;
952 p_dst_a = p_region->picture.A_PIXELS;
953 i_pitch = p_region->picture.A_PITCH;
955 /* Initialize the region pixels */
956 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
958 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
959 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
960 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
961 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
965 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
966 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
967 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
968 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
970 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
971 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
973 DrawBlack( p_line, i_width, p_region, 0, 0);
974 DrawBlack( p_line, i_width, p_region, -1, 0);
975 DrawBlack( p_line, i_width, p_region, 0, -1);
976 DrawBlack( p_line, i_width, p_region, 1, 0);
977 DrawBlack( p_line, i_width, p_region, 0, 1);
980 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
982 DrawBlack( p_line, i_width, p_region, -1, -1);
983 DrawBlack( p_line, i_width, p_region, -1, 1);
984 DrawBlack( p_line, i_width, p_region, 1, -1);
985 DrawBlack( p_line, i_width, p_region, 1, 1);
987 DrawBlack( p_line, i_width, p_region, -2, 0);
988 DrawBlack( p_line, i_width, p_region, 0, -2);
989 DrawBlack( p_line, i_width, p_region, 2, 0);
990 DrawBlack( p_line, i_width, p_region, 0, 2);
992 DrawBlack( p_line, i_width, p_region, -2, -2);
993 DrawBlack( p_line, i_width, p_region, -2, 2);
994 DrawBlack( p_line, i_width, p_region, 2, -2);
995 DrawBlack( p_line, i_width, p_region, 2, 2);
997 DrawBlack( p_line, i_width, p_region, -3, 0);
998 DrawBlack( p_line, i_width, p_region, 0, -3);
999 DrawBlack( p_line, i_width, p_region, 3, 0);
1000 DrawBlack( p_line, i_width, p_region, 0, 3);
1003 for( ; p_line != NULL; p_line = p_line->p_next )
1005 int i_glyph_tmax = 0;
1006 int i_bitmap_offset, i_offset, i_align_offset = 0;
1007 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1009 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1010 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1013 if( p_line->i_width < i_width )
1015 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1017 i_align_offset = i_width - p_line->i_width;
1019 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1021 i_align_offset = ( i_width - p_line->i_width ) / 2;
1025 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1027 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1029 i_offset = ( p_line->p_glyph_pos[ i ].y +
1030 i_glyph_tmax - p_glyph->top + 3 ) *
1031 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1034 if( p_line->b_new_color_mode )
1036 /* Every glyph can (and in fact must) have its own color */
1037 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1040 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1042 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1044 uint8_t i_y_local = i_y;
1045 uint8_t i_u_local = i_u;
1046 uint8_t i_v_local = i_v;
1048 if( p_line->p_fg_bg_ratio != 0x00 )
1050 int i_split = p_glyph->bitmap.width *
1051 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1055 YUVFromRGB( p_line->p_bg_rgb[ i ],
1056 &i_y_local, &i_u_local, &i_v_local );
1060 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1062 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1063 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1065 p_dst_u[i_offset+x] = i_u;
1066 p_dst_v[i_offset+x] = i_v;
1068 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1069 p_dst_a[i_offset+x] = 0xff;
1072 i_offset += i_pitch;
1075 if( p_line->pi_underline_thickness[ i ] )
1077 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1078 p_line->pi_underline_offset[ i ],
1079 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1080 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1081 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1082 i_glyph_tmax, i_align_offset,
1089 /* Apply the alpha setting */
1090 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1091 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1097 * This function renders a text subpicture region into another one.
1098 * It also calculates the size needed for this string, and renders the
1099 * needed glyphs into memory. It is used as pf_add_string callback in
1100 * the vout method by this module
1102 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1103 subpicture_region_t *p_region_in )
1105 filter_sys_t *p_sys = p_filter->p_sys;
1106 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1107 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1108 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1109 int i_string_length;
1111 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1112 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1122 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1123 psz_string = p_region_in->psz_text;
1124 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1126 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1127 i_scale = val.i_int;
1129 if( p_region_in->p_style )
1131 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1132 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1133 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1137 i_font_color = p_sys->i_font_color;
1138 i_font_alpha = 255 - p_sys->i_font_opacity;
1139 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1142 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1143 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1144 SetFontSize( p_filter, i_font_size );
1146 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1147 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1148 i_blue = i_font_color & 0x000000FF;
1150 result.x = result.y = 0;
1151 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1153 psz_unicode = psz_unicode_orig =
1154 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1155 if( psz_unicode == NULL )
1157 #if defined(WORDS_BIGENDIAN)
1158 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1160 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1162 if( iconv_handle == (vlc_iconv_t)-1 )
1164 msg_Warn( p_filter, "unable to do conversion" );
1170 const char *p_in_buffer = psz_string;
1171 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1172 i_in_bytes = strlen( psz_string );
1173 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1174 i_out_bytes_left = i_out_bytes;
1175 p_out_buffer = (char *)psz_unicode;
1176 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1178 &p_out_buffer, &i_out_bytes_left );
1180 vlc_iconv_close( iconv_handle );
1184 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1185 "bytes left %u", (unsigned)i_in_bytes );
1188 *(uint32_t*)p_out_buffer = 0;
1189 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1192 #if defined(HAVE_FRIBIDI)
1194 uint32_t *p_fribidi_string;
1195 int32_t start_pos, pos = 0;
1197 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1198 if( !p_fribidi_string )
1201 /* Do bidi conversion line-by-line */
1202 while( pos < i_string_length )
1204 while( pos < i_string_length )
1206 i_char = psz_unicode[pos];
1207 if (i_char != '\r' && i_char != '\n')
1209 p_fribidi_string[pos] = i_char;
1213 while( pos < i_string_length )
1215 i_char = psz_unicode[pos];
1216 if (i_char == '\r' || i_char == '\n')
1220 if (pos > start_pos)
1222 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1223 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1226 (FriBidiChar*)p_fribidi_string + start_pos,
1231 free( psz_unicode_orig );
1232 psz_unicode = psz_unicode_orig = p_fribidi_string;
1233 p_fribidi_string[ i_string_length ] = 0;
1237 /* Calculate relative glyph positions and a bounding box for the
1239 if( !(p_line = NewLine( strlen( psz_string ))) )
1242 i_pen_x = i_pen_y = 0;
1244 psz_line_start = psz_unicode;
1246 #define face p_sys->p_face
1247 #define glyph face->glyph
1249 while( *psz_unicode )
1251 i_char = *psz_unicode++;
1252 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1257 if( i_char == '\n' )
1259 psz_line_start = psz_unicode;
1260 if( !(p_next = NewLine( strlen( psz_string ))) )
1262 p_line->p_next = p_next;
1263 p_line->i_width = line.xMax;
1264 p_line->i_height = face->size->metrics.height >> 6;
1265 p_line->pp_glyphs[ i ] = NULL;
1266 p_line->i_alpha = i_font_alpha;
1267 p_line->i_red = i_red;
1268 p_line->i_green = i_green;
1269 p_line->i_blue = i_blue;
1272 result.x = __MAX( result.x, line.xMax );
1273 result.y += face->size->metrics.height >> 6;
1276 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1277 i_pen_y += face->size->metrics.height >> 6;
1279 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1284 i_glyph_index = FT_Get_Char_Index( face, i_char );
1285 if( p_sys->i_use_kerning && i_glyph_index
1289 FT_Get_Kerning( face, i_previous, i_glyph_index,
1290 ft_kerning_default, &delta );
1291 i_pen_x += delta.x >> 6;
1294 p_line->p_glyph_pos[ i ].x = i_pen_x;
1295 p_line->p_glyph_pos[ i ].y = i_pen_y;
1296 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1299 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1303 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1306 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1310 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1311 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1314 FT_Done_Glyph( tmp_glyph );
1317 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1320 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1321 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1322 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1324 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1325 p_line->pp_glyphs[ i ] = NULL;
1327 p_line = NewLine( strlen( psz_string ));
1328 if( p_prev ) p_prev->p_next = p_line;
1329 else p_lines = p_line;
1331 uint32_t *psz_unicode_saved = psz_unicode;
1332 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1336 if( psz_unicode == psz_line_start )
1337 { /* try harder to break that line */
1338 psz_unicode = psz_unicode_saved;
1339 while( psz_unicode > psz_line_start &&
1340 *psz_unicode != '_' && *psz_unicode != '/' &&
1341 *psz_unicode != '\\' && *psz_unicode != '.' )
1346 if( psz_unicode == psz_line_start )
1348 msg_Warn( p_filter, "unbreakable string" );
1353 *psz_unicode = '\n';
1355 psz_unicode = psz_line_start;
1358 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1361 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1362 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1364 i_previous = i_glyph_index;
1365 i_pen_x += glyph->advance.x >> 6;
1369 p_line->i_width = line.xMax;
1370 p_line->i_height = face->size->metrics.height >> 6;
1371 p_line->pp_glyphs[ i ] = NULL;
1372 p_line->i_alpha = i_font_alpha;
1373 p_line->i_red = i_red;
1374 p_line->i_green = i_green;
1375 p_line->i_blue = i_blue;
1376 result.x = __MAX( result.x, line.xMax );
1377 result.y += line.yMax - line.yMin;
1382 p_region_out->i_x = p_region_in->i_x;
1383 p_region_out->i_y = p_region_in->i_y;
1385 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1386 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1388 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1390 free( psz_unicode_orig );
1391 FreeLines( p_lines );
1395 free( psz_unicode_orig );
1396 FreeLines( p_lines );
1397 return VLC_EGENERIC;
1400 #ifdef HAVE_FONTCONFIG
1401 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1402 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1403 bool b_italic, bool b_uline )
1405 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1409 p_style->i_font_size = i_font_size;
1410 p_style->i_font_color = i_font_color;
1411 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1412 p_style->b_italic = b_italic;
1413 p_style->b_bold = b_bold;
1414 p_style->b_underline = b_uline;
1416 p_style->psz_fontname = strdup( psz_fontname );
1421 static void DeleteStyle( ft_style_t *p_style )
1425 free( p_style->psz_fontname );
1430 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1437 if(( s1->i_font_size == s2->i_font_size ) &&
1438 ( s1->i_font_color == s2->i_font_color ) &&
1439 ( s1->b_italic == s2->b_italic ) &&
1440 ( s1->b_bold == s2->b_bold ) &&
1441 ( s1->b_underline == s2->b_underline ) &&
1442 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1449 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1450 uint32_t i_color, uint32_t i_karaoke_bg_color )
1452 font_stack_t *p_new;
1455 return VLC_EGENERIC;
1457 p_new = malloc( sizeof( font_stack_t ) );
1461 p_new->p_next = NULL;
1464 p_new->psz_name = strdup( psz_name );
1466 p_new->psz_name = NULL;
1468 p_new->i_size = i_size;
1469 p_new->i_color = i_color;
1470 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1478 font_stack_t *p_last;
1480 for( p_last = *p_font;
1482 p_last = p_last->p_next )
1485 p_last->p_next = p_new;
1490 static int PopFont( font_stack_t **p_font )
1492 font_stack_t *p_last, *p_next_to_last;
1494 if( !p_font || !*p_font )
1495 return VLC_EGENERIC;
1497 p_next_to_last = NULL;
1498 for( p_last = *p_font;
1500 p_last = p_last->p_next )
1502 p_next_to_last = p_last;
1505 if( p_next_to_last )
1506 p_next_to_last->p_next = NULL;
1510 free( p_last->psz_name );
1516 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1517 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1519 font_stack_t *p_last;
1521 if( !p_font || !*p_font )
1522 return VLC_EGENERIC;
1524 for( p_last=*p_font;
1526 p_last=p_last->p_next )
1529 *psz_name = p_last->psz_name;
1530 *i_size = p_last->i_size;
1531 *i_color = p_last->i_color;
1532 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1537 static void IconvText( filter_t *p_filter, const char *psz_string,
1538 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1540 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1542 /* If memory hasn't been allocated for our output string, allocate it here
1543 * - the calling function must now be responsible for freeing it.
1545 if( !*ppsz_unicode )
1546 *ppsz_unicode = (uint32_t *)
1547 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1549 /* We don't need to handle a NULL pointer in *ppsz_unicode
1550 * if we are instead testing for a non NULL value like we are here */
1554 #if defined(WORDS_BIGENDIAN)
1555 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1557 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1559 if( iconv_handle != (vlc_iconv_t)-1 )
1561 char *p_in_buffer, *p_out_buffer;
1562 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1563 i_in_bytes = strlen( psz_string );
1564 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1565 i_out_bytes_left = i_out_bytes;
1566 p_in_buffer = (char *) psz_string;
1567 p_out_buffer = (char *) *ppsz_unicode;
1568 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1569 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1571 vlc_iconv_close( iconv_handle );
1575 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1576 "bytes left %u", (unsigned)i_in_bytes );
1580 *(uint32_t*)p_out_buffer = 0;
1582 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1587 msg_Warn( p_filter, "unable to do conversion" );
1592 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1593 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1596 ft_style_t *p_style = NULL;
1598 char *psz_fontname = NULL;
1599 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1600 uint32_t i_karaoke_bg_color = i_font_color;
1601 int i_font_size = p_sys->i_font_size;
1603 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1604 &i_font_color, &i_karaoke_bg_color ))
1606 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1607 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1612 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1613 bool b_uline, int i_karaoke_bgcolor,
1614 line_desc_t *p_line, uint32_t *psz_unicode,
1615 int *pi_pen_x, int i_pen_y, int *pi_start,
1616 FT_Vector *p_result )
1621 bool b_first_on_line = true;
1624 int i_pen_x_start = *pi_pen_x;
1626 uint32_t *psz_unicode_start = psz_unicode;
1628 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1630 /* Account for part of line already in position */
1631 for( i=0; i<*pi_start; i++ )
1635 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1636 ft_glyph_bbox_pixels, &glyph_size );
1638 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1639 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1640 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1641 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1647 b_first_on_line = false;
1649 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1655 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1656 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1660 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1661 ft_kerning_default, &delta );
1662 *pi_pen_x += delta.x >> 6;
1664 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1665 p_line->p_glyph_pos[ i ].y = i_pen_y;
1667 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1671 "unable to render text FT_Load_Glyph returned %d", i_error );
1672 p_line->pp_glyphs[ i ] = NULL;
1673 return VLC_EGENERIC;
1675 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1679 "unable to render text FT_Get_Glyph returned %d", i_error );
1680 p_line->pp_glyphs[ i ] = NULL;
1681 return VLC_EGENERIC;
1683 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1684 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1687 FT_Done_Glyph( tmp_glyph );
1692 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1693 p_face->size->metrics.y_scale));
1694 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1695 p_face->size->metrics.y_scale));
1697 p_line->pi_underline_offset[ i ] =
1698 ( aOffset < 0 ) ? -aOffset : aOffset;
1699 p_line->pi_underline_thickness[ i ] =
1700 ( aSize < 0 ) ? -aSize : aSize;
1702 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1703 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1704 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1705 p_line->p_fg_bg_ratio[ i ] = 0x00;
1707 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1708 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1709 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1711 while( --i > *pi_start )
1713 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1716 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1720 if( psz_unicode == psz_unicode_start )
1722 if( b_first_on_line )
1724 msg_Warn( p_filter, "unbreakable string" );
1725 p_line->pp_glyphs[ i ] = NULL;
1726 return VLC_EGENERIC;
1728 *pi_pen_x = i_pen_x_start;
1730 p_line->i_width = line.xMax;
1731 p_line->i_height = __MAX( p_line->i_height,
1732 p_face->size->metrics.height >> 6 );
1733 p_line->pp_glyphs[ i ] = NULL;
1735 p_result->x = __MAX( p_result->x, line.xMax );
1736 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1737 i_yMax - i_yMin ) );
1744 *psz_unicode = '\n';
1746 psz_unicode = psz_unicode_start;
1747 *pi_pen_x = i_pen_x_start;
1755 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1756 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1758 i_previous = i_glyph_index;
1759 *pi_pen_x += p_face->glyph->advance.x >> 6;
1762 p_line->i_width = line.xMax;
1763 p_line->i_height = __MAX( p_line->i_height,
1764 p_face->size->metrics.height >> 6 );
1765 p_line->pp_glyphs[ i ] = NULL;
1767 p_result->x = __MAX( p_result->x, line.xMax );
1768 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1769 line.yMax - line.yMin ) );
1773 /* Get rid of any text processed - if necessary repositioning
1774 * at the start of a new line of text
1778 *psz_unicode_start = '\0';
1780 else if( psz_unicode > psz_unicode_start )
1782 for( i=0; psz_unicode[ i ]; i++ )
1783 psz_unicode_start[ i ] = psz_unicode[ i ];
1784 psz_unicode_start[ i ] = '\0';
1790 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1791 font_stack_t **p_fonts, int i_scale )
1794 char *psz_fontname = NULL;
1795 uint32_t i_font_color = 0xffffff;
1796 int i_font_alpha = 0;
1797 uint32_t i_karaoke_bg_color = 0x00ffffff;
1798 int i_font_size = 24;
1800 /* Default all attributes to the top font in the stack -- in case not
1801 * all attributes are specified in the sub-font
1803 if( VLC_SUCCESS == PeekFont( p_fonts,
1807 &i_karaoke_bg_color ))
1809 psz_fontname = strdup( psz_fontname );
1810 i_font_size = i_font_size * 1000 / i_scale;
1812 i_font_alpha = (i_font_color >> 24) & 0xff;
1813 i_font_color &= 0x00ffffff;
1815 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1817 char *psz_name = xml_ReaderName( p_xml_reader );
1818 char *psz_value = xml_ReaderValue( p_xml_reader );
1820 if( psz_name && psz_value )
1822 if( !strcasecmp( "face", psz_name ) )
1824 free( psz_fontname );
1825 psz_fontname = strdup( psz_value );
1827 else if( !strcasecmp( "size", psz_name ) )
1829 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1831 int i_value = atoi( psz_value );
1833 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1834 i_font_size += ( i_value * i_font_size ) / 10;
1835 else if( i_value < -5 )
1836 i_font_size = - i_value;
1837 else if( i_value > 5 )
1838 i_font_size = i_value;
1841 i_font_size = atoi( psz_value );
1843 else if( !strcasecmp( "color", psz_name ) &&
1844 ( psz_value[0] == '#' ) )
1846 i_font_color = strtol( psz_value + 1, NULL, 16 );
1847 i_font_color &= 0x00ffffff;
1849 else if( !strcasecmp( "alpha", psz_name ) &&
1850 ( psz_value[0] == '#' ) )
1852 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1853 i_font_alpha &= 0xff;
1859 rv = PushFont( p_fonts,
1861 i_font_size * i_scale / 1000,
1862 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1863 i_karaoke_bg_color );
1865 free( psz_fontname );
1870 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1871 uint32_t **psz_text_out, uint32_t *pi_runs,
1872 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1873 ft_style_t *p_style )
1875 uint32_t i_string_length = 0;
1877 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1878 *psz_text_out += i_string_length;
1880 if( ppp_styles && ppi_run_lengths )
1886 *ppp_styles = (ft_style_t **)
1887 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1889 else if( *pi_runs == 1 )
1891 *ppp_styles = (ft_style_t **)
1892 malloc( *pi_runs * sizeof( ft_style_t * ) );
1895 /* We have just malloc'ed this memory successfully -
1896 * *pi_runs HAS to be within the memory area of *ppp_styles */
1899 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1903 if( *ppi_run_lengths )
1905 *ppi_run_lengths = (uint32_t *)
1906 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1908 else if( *pi_runs == 1 )
1910 *ppi_run_lengths = (uint32_t *)
1911 malloc( *pi_runs * sizeof( uint32_t ) );
1914 /* same remarks here */
1915 if( *ppi_run_lengths )
1917 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1920 /* If we couldn't use the p_style argument due to memory allocation
1921 * problems above, release it here.
1923 if( p_style ) DeleteStyle( p_style );
1926 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1927 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1929 /* Karaoke tags _PRECEDE_ the text they specify a duration
1930 * for, therefore we are working out the length for the
1931 * previous tag, and first time through we have nothing
1933 if( pi_k_run_lengths )
1938 /* Work out how many characters are presently in the string
1940 for( i = 0; i < i_runs; i++ )
1941 i_chars += pi_run_lengths[ i ];
1943 /* Subtract away those we've already allocated to other
1946 for( i = 0; i < i_k_runs; i++ )
1947 i_chars -= pi_k_run_lengths[ i ];
1949 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1953 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1954 uint32_t **ppi_k_run_lengths,
1955 uint32_t **ppi_k_durations )
1957 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1959 char *psz_name = xml_ReaderName( p_xml_reader );
1960 char *psz_value = xml_ReaderValue( p_xml_reader );
1962 if( psz_name && psz_value &&
1963 !strcasecmp( "t", psz_name ) )
1965 if( ppi_k_durations && ppi_k_run_lengths )
1969 if( *ppi_k_durations )
1971 *ppi_k_durations = (uint32_t *)
1972 realloc( *ppi_k_durations,
1973 *pi_k_runs * sizeof( uint32_t ) );
1975 else if( *pi_k_runs == 1 )
1977 *ppi_k_durations = (uint32_t *)
1978 malloc( *pi_k_runs * sizeof( uint32_t ) );
1981 if( *ppi_k_run_lengths )
1983 *ppi_k_run_lengths = (uint32_t *)
1984 realloc( *ppi_k_run_lengths,
1985 *pi_k_runs * sizeof( uint32_t ) );
1987 else if( *pi_k_runs == 1 )
1989 *ppi_k_run_lengths = (uint32_t *)
1990 malloc( *pi_k_runs * sizeof( uint32_t ) );
1992 if( *ppi_k_durations )
1993 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1995 if( *ppi_k_run_lengths )
1996 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
2004 static int ProcessNodes( filter_t *p_filter,
2005 xml_reader_t *p_xml_reader,
2006 text_style_t *p_font_style,
2011 uint32_t **ppi_run_lengths,
2012 ft_style_t ***ppp_styles,
2015 uint32_t *pi_k_runs,
2016 uint32_t **ppi_k_run_lengths,
2017 uint32_t **ppi_k_durations )
2019 int rv = VLC_SUCCESS;
2020 filter_sys_t *p_sys = p_filter->p_sys;
2021 uint32_t *psz_text_orig = psz_text;
2022 font_stack_t *p_fonts = NULL;
2026 char *psz_node = NULL;
2028 bool b_italic = false;
2029 bool b_bold = false;
2030 bool b_uline = false;
2032 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2033 i_scale = val.i_int;
2037 rv = PushFont( &p_fonts,
2038 p_font_style->psz_fontname,
2039 p_font_style->i_font_size * i_scale / 1000,
2040 (p_font_style->i_font_color & 0xffffff) |
2041 ((p_font_style->i_font_alpha & 0xff) << 24),
2042 (p_font_style->i_karaoke_background_color & 0xffffff) |
2043 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2045 if( p_font_style->i_style_flags & STYLE_BOLD )
2047 if( p_font_style->i_style_flags & STYLE_ITALIC )
2049 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2054 rv = PushFont( &p_fonts,
2060 if( rv != VLC_SUCCESS )
2063 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2065 switch ( xml_ReaderNodeType( p_xml_reader ) )
2067 case XML_READER_NONE:
2069 case XML_READER_ENDELEM:
2070 psz_node = xml_ReaderName( p_xml_reader );
2074 if( !strcasecmp( "font", psz_node ) )
2075 PopFont( &p_fonts );
2076 else if( !strcasecmp( "b", psz_node ) )
2078 else if( !strcasecmp( "i", psz_node ) )
2080 else if( !strcasecmp( "u", psz_node ) )
2086 case XML_READER_STARTELEM:
2087 psz_node = xml_ReaderName( p_xml_reader );
2090 if( !strcasecmp( "font", psz_node ) )
2091 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2092 else if( !strcasecmp( "b", psz_node ) )
2094 else if( !strcasecmp( "i", psz_node ) )
2096 else if( !strcasecmp( "u", psz_node ) )
2098 else if( !strcasecmp( "br", psz_node ) )
2100 SetupLine( p_filter, "\n", &psz_text,
2101 pi_runs, ppi_run_lengths, ppp_styles,
2102 GetStyleFromFontStack( p_sys,
2108 else if( !strcasecmp( "k", psz_node ) )
2110 /* Only valid in karaoke */
2113 if( *pi_k_runs > 0 )
2115 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2116 *pi_k_runs, *ppi_k_run_lengths );
2118 SetupKaraoke( p_xml_reader, pi_k_runs,
2119 ppi_k_run_lengths, ppi_k_durations );
2126 case XML_READER_TEXT:
2127 psz_node = xml_ReaderValue( p_xml_reader );
2130 /* Turn any multiple-whitespaces into single spaces */
2131 char *s = strpbrk( psz_node, "\t\r\n " );
2134 int i_whitespace = strspn( s, "\t\r\n " );
2136 if( i_whitespace > 1 )
2139 strlen( s ) - i_whitespace + 1 );
2142 s = strpbrk( s, "\t\r\n " );
2144 SetupLine( p_filter, psz_node, &psz_text,
2145 pi_runs, ppi_run_lengths, ppp_styles,
2146 GetStyleFromFontStack( p_sys,
2155 if( rv != VLC_SUCCESS )
2157 psz_text = psz_text_orig;
2163 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2164 *pi_k_runs, *ppi_k_run_lengths );
2167 *pi_len = psz_text - psz_text_orig;
2169 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2174 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2178 for( k=0; k < p_sys->i_font_attachments; k++ )
2180 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2182 FT_Face p_face = NULL;
2184 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2192 bool match = !strcasecmp( p_face->family_name,
2193 p_style->psz_fontname );
2195 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2196 match = match && p_style->b_bold;
2198 match = match && !p_style->b_bold;
2200 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2201 match = match && p_style->b_italic;
2203 match = match && !p_style->b_italic;
2211 FT_Done_Face( p_face );
2216 return VLC_EGENERIC;
2219 static int ProcessLines( filter_t *p_filter,
2224 uint32_t *pi_run_lengths,
2225 ft_style_t **pp_styles,
2226 line_desc_t **pp_lines,
2228 FT_Vector *p_result,
2232 uint32_t *pi_k_run_lengths,
2233 uint32_t *pi_k_durations )
2235 filter_sys_t *p_sys = p_filter->p_sys;
2236 ft_style_t **pp_char_styles;
2237 int *p_new_positions = NULL;
2238 int8_t *p_levels = NULL;
2239 uint8_t *pi_karaoke_bar = NULL;
2243 /* Assign each character in the text string its style explicitly, so that
2244 * after the characters have been shuffled around by Fribidi, we can re-apply
2245 * the styles, and to simplify the calculation of runs within a line.
2247 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2248 if( !pp_char_styles )
2253 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2254 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2255 * we just won't be able to display the progress bar; at least we'll
2261 for( j = 0; j < i_runs; j++ )
2262 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2263 pp_char_styles[ i++ ] = pp_styles[ j ];
2265 #if defined(HAVE_FRIBIDI)
2267 ft_style_t **pp_char_styles_new;
2268 int *p_old_positions;
2269 uint32_t *p_fribidi_string;
2270 int start_pos, pos = 0;
2272 pp_char_styles_new = (ft_style_t **)
2273 malloc( i_len * sizeof( ft_style_t * ));
2275 p_fribidi_string = (uint32_t *)
2276 malloc( (i_len + 1) * sizeof(uint32_t) );
2277 p_old_positions = (int *)
2278 malloc( (i_len + 1) * sizeof( int ) );
2279 p_new_positions = (int *)
2280 malloc( (i_len + 1) * sizeof( int ) );
2281 p_levels = (int8_t *)
2282 malloc( (i_len + 1) * sizeof( int8_t ) );
2284 if( ! pp_char_styles_new ||
2285 ! p_fribidi_string ||
2286 ! p_old_positions ||
2287 ! p_new_positions ||
2291 free( p_old_positions );
2292 free( p_new_positions );
2293 free( p_fribidi_string );
2294 free( pp_char_styles_new );
2295 free( pi_karaoke_bar );
2297 free( pp_char_styles );
2301 /* Do bidi conversion line-by-line */
2304 while(pos < i_len) {
2305 if (psz_text[pos] != '\n')
2307 p_fribidi_string[pos] = psz_text[pos];
2308 pp_char_styles_new[pos] = pp_char_styles[pos];
2309 p_new_positions[pos] = pos;
2314 while(pos < i_len) {
2315 if (psz_text[pos] == '\n')
2319 if (pos > start_pos)
2321 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2322 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2323 pos - start_pos, &base_dir,
2324 (FriBidiChar*)p_fribidi_string + start_pos,
2325 p_new_positions + start_pos,
2327 p_levels + start_pos );
2328 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2330 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2331 p_old_positions[ j - start_pos ] ];
2332 p_new_positions[ j ] += start_pos;
2336 free( p_old_positions );
2337 free( pp_char_styles );
2338 pp_char_styles = pp_char_styles_new;
2339 psz_text = p_fribidi_string;
2340 p_fribidi_string[ i_len ] = 0;
2343 /* Work out the karaoke */
2344 if( pi_karaoke_bar )
2346 int64_t i_last_duration = 0;
2347 int64_t i_duration = 0;
2348 int64_t i_start_pos = 0;
2349 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2351 for( k = 0; k< i_k_runs; k++ )
2353 double fraction = 0.0;
2355 i_duration += pi_k_durations[ k ];
2357 if( i_duration < i_elapsed )
2359 /* Completely finished this run-length -
2360 * let it render normally */
2364 else if( i_elapsed < i_last_duration )
2366 /* Haven't got up to this segment yet -
2367 * render it completely in karaoke BG mode */
2373 /* Partway through this run */
2375 fraction = (double)(i_elapsed - i_last_duration) /
2376 (double)pi_k_durations[ k ];
2378 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2380 double shade = pi_k_run_lengths[ k ] * fraction;
2382 if( p_new_positions )
2383 j = p_new_positions[ i_start_pos + i ];
2385 j = i_start_pos + i;
2387 if( i < (uint32_t)shade )
2388 pi_karaoke_bar[ j ] = 0xff;
2389 else if( (double)i > shade )
2390 pi_karaoke_bar[ j ] = 0x00;
2393 shade -= (int)shade;
2394 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2395 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2399 i_last_duration = i_duration;
2400 i_start_pos += pi_k_run_lengths[ k ];
2404 free( p_new_positions );
2406 FT_Vector tmp_result;
2408 line_desc_t *p_line = NULL;
2409 line_desc_t *p_prev = NULL;
2415 p_result->x = p_result->y = 0;
2416 tmp_result.x = tmp_result.y = 0;
2419 for( k = 0; k <= (uint32_t) i_len; k++ )
2421 if( ( k == (uint32_t) i_len ) ||
2423 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2425 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2427 /* End of the current style run */
2428 FT_Face p_face = NULL;
2431 /* Look for a match amongst our attachments first */
2432 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2436 char *psz_fontfile = NULL;
2438 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2439 if( p_sys->b_fontconfig_ok )
2441 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2442 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2443 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2444 p_style->psz_fontname,
2449 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2451 if( psz_fontfile && ! *psz_fontfile )
2453 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2454 " so using default font", p_style->psz_fontname,
2455 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2456 (p_style->b_bold ? "(Bold)" :
2457 (p_style->b_italic ? "(Italic)" : ""))) );
2458 free( psz_fontfile );
2459 psz_fontfile = NULL;
2464 if( FT_New_Face( p_sys->p_library,
2465 psz_fontfile, i_idx, &p_face ) )
2467 free( psz_fontfile );
2468 free( pp_char_styles );
2469 #if defined(HAVE_FRIBIDI)
2472 free( pi_karaoke_bar );
2473 return VLC_EGENERIC;
2475 free( psz_fontfile );
2479 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2481 /* We've loaded a font face which is unhelpful for actually
2482 * rendering text - fallback to the default one.
2484 FT_Done_Face( p_face );
2488 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2489 ft_encoding_unicode ) ||
2490 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2491 p_style->i_font_size ) )
2493 if( p_face ) FT_Done_Face( p_face );
2494 free( pp_char_styles );
2495 #if defined(HAVE_FRIBIDI)
2498 free( pi_karaoke_bar );
2499 return VLC_EGENERIC;
2501 p_sys->i_use_kerning =
2502 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2505 uint32_t *psz_unicode = (uint32_t *)
2506 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2509 if( p_face ) FT_Done_Face( p_face );
2510 free( pp_char_styles );
2511 free( psz_unicode );
2512 #if defined(HAVE_FRIBIDI)
2515 free( pi_karaoke_bar );
2518 memcpy( psz_unicode, psz_text + i_prev,
2519 sizeof( uint32_t ) * ( k - i_prev ) );
2520 psz_unicode[ k - i_prev ] = 0;
2521 while( *psz_unicode )
2525 if( !(p_line = NewLine( i_len - i_prev)) )
2527 if( p_face ) FT_Done_Face( p_face );
2528 free( pp_char_styles );
2529 free( psz_unicode );
2530 #if defined(HAVE_FRIBIDI)
2533 free( pi_karaoke_bar );
2536 /* New Color mode only works in YUVA rendering mode --
2537 * (RGB mode has palette constraints on it). We therefore
2538 * need to populate the legacy colour fields also.
2540 p_line->b_new_color_mode = true;
2541 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2542 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2543 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2544 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2545 p_line->p_next = NULL;
2547 i_pen_y += tmp_result.y;
2551 if( p_prev ) p_prev->p_next = p_line;
2552 else *pp_lines = p_line;
2555 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2556 p_style->i_font_color, p_style->b_underline,
2557 p_style->i_karaoke_bg_color,
2558 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2559 &tmp_result ) != VLC_SUCCESS )
2561 if( p_face ) FT_Done_Face( p_face );
2562 free( pp_char_styles );
2563 free( psz_unicode );
2564 #if defined(HAVE_FRIBIDI)
2567 free( pi_karaoke_bar );
2568 return VLC_EGENERIC;
2573 p_result->x = __MAX( p_result->x, tmp_result.x );
2574 p_result->y += tmp_result.y;
2579 if( *psz_unicode == '\n')
2583 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2585 *c_ptr = *(c_ptr+1);
2590 free( psz_unicode );
2591 if( p_face ) FT_Done_Face( p_face );
2595 free( pp_char_styles );
2596 #if defined(HAVE_FRIBIDI)
2601 p_result->x = __MAX( p_result->x, tmp_result.x );
2602 p_result->y += tmp_result.y;
2605 if( pi_karaoke_bar )
2608 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2610 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2612 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2616 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2618 /* 100% BG colour will render faster if we
2619 * instead make it 100% FG colour, so leave
2620 * the ratio alone and copy the value across
2622 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2626 if( pi_karaoke_bar[ i ] & 0x80 )
2628 /* Swap Left and Right sides over for Right aligned
2629 * language text (eg. Arabic, Hebrew)
2631 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2633 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2634 p_line->p_bg_rgb[ k ] = i_tmp;
2636 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2639 /* Jump over the '\n' at the line-end */
2642 free( pi_karaoke_bar );
2648 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2649 subpicture_region_t *p_region_in )
2651 int rv = VLC_SUCCESS;
2652 stream_t *p_sub = NULL;
2653 xml_t *p_xml = NULL;
2654 xml_reader_t *p_xml_reader = NULL;
2656 if( !p_region_in || !p_region_in->psz_html )
2657 return VLC_EGENERIC;
2659 /* Reset the default fontsize in case screen metrics have changed */
2660 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2662 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2663 (uint8_t *) p_region_in->psz_html,
2664 strlen( p_region_in->psz_html ),
2668 p_xml = xml_Create( p_filter );
2671 bool b_karaoke = false;
2673 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2676 /* Look for Root Node */
2677 if( xml_ReaderRead( p_xml_reader ) == 1 )
2679 char *psz_node = xml_ReaderName( p_xml_reader );
2681 if( !strcasecmp( "karaoke", psz_node ) )
2683 /* We're going to have to render the text a number
2684 * of times to show the progress marker on the text.
2686 var_SetBool( p_filter, "text-rerender", true );
2689 else if( !strcasecmp( "text", psz_node ) )
2695 /* Only text and karaoke tags are supported */
2696 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2697 xml_ReaderDelete( p_xml, p_xml_reader );
2698 p_xml_reader = NULL;
2710 uint32_t i_runs = 0;
2711 uint32_t i_k_runs = 0;
2712 uint32_t *pi_run_lengths = NULL;
2713 uint32_t *pi_k_run_lengths = NULL;
2714 uint32_t *pi_k_durations = NULL;
2715 ft_style_t **pp_styles = NULL;
2717 line_desc_t *p_lines = NULL;
2719 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2720 sizeof( uint32_t ) );
2725 rv = ProcessNodes( p_filter, p_xml_reader,
2726 p_region_in->p_style, psz_text, &i_len,
2727 &i_runs, &pi_run_lengths, &pp_styles,
2728 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2731 p_region_out->i_x = p_region_in->i_x;
2732 p_region_out->i_y = p_region_in->i_y;
2734 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2736 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2737 pi_run_lengths, pp_styles, &p_lines, &result,
2738 b_karaoke, i_k_runs, pi_k_run_lengths,
2742 for( k=0; k<i_runs; k++)
2743 DeleteStyle( pp_styles[k] );
2745 free( pi_run_lengths );
2748 /* Don't attempt to render text that couldn't be layed out
2751 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2753 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2755 Render( p_filter, p_region_out, p_lines,
2756 result.x, result.y );
2760 RenderYUVA( p_filter, p_region_out, p_lines,
2761 result.x, result.y );
2765 FreeLines( p_lines );
2767 xml_ReaderDelete( p_xml, p_xml_reader );
2769 xml_Delete( p_xml );
2771 stream_Delete( p_sub );
2777 static char* FontConfig_Select( FcConfig* priv, const char* family,
2778 bool b_bold, bool b_italic, int *i_idx )
2781 FcPattern *pat, *p_pat;
2785 pat = FcPatternCreate();
2786 if (!pat) return NULL;
2788 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2789 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2790 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2791 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2793 FcDefaultSubstitute( pat );
2795 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2797 FcPatternDestroy( pat );
2801 p_pat = FcFontMatch( priv, pat, &result );
2802 FcPatternDestroy( pat );
2803 if( !p_pat ) return NULL;
2805 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2806 || ( val_b != FcTrue ) )
2808 FcPatternDestroy( p_pat );
2811 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2816 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2818 FcPatternDestroy( p_pat );
2823 if( strcasecmp((const char*)val_s, family ) != 0 )
2824 msg_Warn( p_filter, "fontconfig: selected font family is not"
2825 "the requested one: '%s' != '%s'\n",
2826 (const char*)val_s, family );
2829 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2831 FcPatternDestroy( p_pat );
2835 FcPatternDestroy( p_pat );
2836 return strdup( (const char*)val_s );
2840 static void FreeLine( line_desc_t *p_line )
2843 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2845 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2847 free( p_line->pp_glyphs );
2848 free( p_line->p_glyph_pos );
2849 free( p_line->p_fg_rgb );
2850 free( p_line->p_bg_rgb );
2851 free( p_line->p_fg_bg_ratio );
2852 free( p_line->pi_underline_offset );
2853 free( p_line->pi_underline_thickness );
2857 static void FreeLines( line_desc_t *p_lines )
2859 line_desc_t *p_line, *p_next;
2861 if( !p_lines ) return;
2863 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2865 p_next = p_line->p_next;
2870 static line_desc_t *NewLine( int i_count )
2872 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2874 if( !p_line ) return NULL;
2875 p_line->i_height = 0;
2876 p_line->i_width = 0;
2877 p_line->p_next = NULL;
2879 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2880 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2881 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2882 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2883 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2884 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2885 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2886 if( ( p_line->pp_glyphs == NULL ) ||
2887 ( p_line->p_glyph_pos == NULL ) ||
2888 ( p_line->p_fg_rgb == NULL ) ||
2889 ( p_line->p_bg_rgb == NULL ) ||
2890 ( p_line->p_fg_bg_ratio == NULL ) ||
2891 ( p_line->pi_underline_offset == NULL ) ||
2892 ( p_line->pi_underline_thickness == NULL ) )
2894 free( p_line->pi_underline_thickness );
2895 free( p_line->pi_underline_offset );
2896 free( p_line->p_fg_rgb );
2897 free( p_line->p_bg_rgb );
2898 free( p_line->p_fg_bg_ratio );
2899 free( p_line->p_glyph_pos );
2900 free( p_line->pp_glyphs );
2904 p_line->pp_glyphs[0] = NULL;
2905 p_line->b_new_color_mode = false;
2910 static int GetFontSize( filter_t *p_filter )
2912 filter_sys_t *p_sys = p_filter->p_sys;
2916 if( p_sys->i_default_font_size )
2918 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2919 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2921 i_size = p_sys->i_default_font_size;
2925 var_Get( p_filter, "freetype-rel-fontsize", &val );
2926 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2927 p_filter->p_sys->i_display_height =
2928 p_filter->fmt_out.video.i_height;
2932 msg_Warn( p_filter, "invalid fontsize, using 12" );
2933 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2934 i_size = 12 * val.i_int / 1000;
2941 static int SetFontSize( filter_t *p_filter, int i_size )
2943 filter_sys_t *p_sys = p_filter->p_sys;
2947 i_size = GetFontSize( p_filter );
2949 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2952 p_sys->i_font_size = i_size;
2954 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2956 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2957 return VLC_EGENERIC;
2963 static void YUVFromRGB( uint32_t i_argb,
2964 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2966 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2967 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2968 int i_blue = ( i_argb & 0x000000ff );
2970 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2971 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2972 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2973 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2974 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2975 -585 * i_blue + 4096 + 1048576) >> 13, 240);