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 const bool b_alive = vlc_object_alive( p_fontbuilder );
499 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
501 /* We wait for the thread on the first FontBuilderDetach */
504 vlc_object_kill( p_fontbuilder );
505 vlc_mutex_unlock( lock );
507 /* We need to unlock otherwise we may not join (the thread waiting
508 * for the lock). It is safe to unlock as no one else will try a
509 * join and we have a reference on the object) */
510 vlc_thread_join( p_fontbuilder );
512 vlc_mutex_lock( lock );
514 vlc_object_release( p_fontbuilder );
516 vlc_mutex_unlock( lock );
518 static void FontBuilderThread( vlc_object_t *p_this )
520 FcConfig *p_fontconfig = FcInitLoadConfig();
522 vlc_thread_ready( p_this );
528 //msg_Dbg( p_this, "Building font database..." );
529 msg_Dbg( p_this, "Building font database..." );
531 if(! FcConfigBuildFonts( p_fontconfig ))
533 /* Don't destroy the fontconfig object - we won't be able to do
534 * italics or bold or change the font face, but we will still
535 * be able to do underline and change the font size.
537 msg_Err( p_this, "fontconfig database can't be built. "
538 "Font styling won't be available" );
542 msg_Dbg( p_this, "Finished building font database." );
543 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
545 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
546 p_this->p_private = p_fontconfig;
547 vlc_mutex_unlock( p_lock );
549 var_SetBool( p_this, "build-done", true );
552 static void FontBuilderDestructor( vlc_object_t *p_this )
554 FcConfig *p_fontconfig = p_this->p_private;
557 FcConfigDestroy( p_fontconfig );
559 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
560 vlc_value_t oldval, vlc_value_t newval, void *param )
562 filter_t *p_filter = param;
563 filter_sys_t *p_sys = p_filter->p_sys;
567 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
569 p_sys->b_fontconfig_ok = true;
570 p_sys->p_fontconfig = p_this->p_private;
572 vlc_mutex_unlock( p_lock );
581 /*****************************************************************************
582 * Make any TTF/OTF fonts present in the attachments of the media file
583 * and store them for later use by the FreeType Engine
584 *****************************************************************************/
585 static int LoadFontsFromAttachments( filter_t *p_filter )
587 filter_sys_t *p_sys = p_filter->p_sys;
588 input_thread_t *p_input;
589 input_attachment_t **pp_attachments;
590 int i_attachments_cnt;
592 int rv = VLC_SUCCESS;
594 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
598 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
600 vlc_object_release(p_input);
604 p_sys->i_font_attachments = 0;
605 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
606 if(! p_sys->pp_font_attachments )
609 for( k = 0; k < i_attachments_cnt; k++ )
611 input_attachment_t *p_attach = pp_attachments[k];
613 if( p_sys->pp_font_attachments )
615 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
616 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
617 ( p_attach->i_data > 0 ) &&
618 ( p_attach->p_data != NULL ) )
620 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
624 vlc_input_attachment_Delete( p_attach );
629 vlc_input_attachment_Delete( p_attach );
632 free( pp_attachments );
634 vlc_object_release(p_input);
639 /*****************************************************************************
640 * Render: place string in picture
641 *****************************************************************************
642 * This function merges the previously rendered freetype glyphs into a picture
643 *****************************************************************************/
644 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
645 line_desc_t *p_line, int i_width, int i_height )
647 static uint8_t pi_gamma[16] =
648 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
649 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
653 int i, x, y, i_pitch;
654 uint8_t i_y; /* YUV values, derived from incoming RGB */
656 subpicture_region_t *p_region_tmp;
658 /* Create a new subpicture region */
659 memset( &fmt, 0, sizeof(video_format_t) );
660 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
662 fmt.i_width = fmt.i_visible_width = i_width + 4;
663 fmt.i_height = fmt.i_visible_height = i_height + 4;
664 if( p_region->fmt.i_visible_width > 0 )
665 fmt.i_visible_width = p_region->fmt.i_visible_width;
666 if( p_region->fmt.i_visible_height > 0 )
667 fmt.i_visible_height = p_region->fmt.i_visible_height;
668 fmt.i_x_offset = fmt.i_y_offset = 0;
669 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
672 msg_Err( p_filter, "cannot allocate SPU region" );
676 p_region->fmt = p_region_tmp->fmt;
677 p_region->picture = p_region_tmp->picture;
678 free( p_region_tmp );
680 /* Calculate text color components */
681 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
682 25 * p_line->i_blue + 128) >> 8) + 16;
683 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
684 112 * p_line->i_blue + 128) >> 8) + 128;
685 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
686 18 * p_line->i_blue + 128) >> 8) + 128;
689 fmt.p_palette->i_entries = 16;
690 for( i = 0; i < 8; i++ )
692 fmt.p_palette->palette[i][0] = 0;
693 fmt.p_palette->palette[i][1] = 0x80;
694 fmt.p_palette->palette[i][2] = 0x80;
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;
699 for( i = 8; i < fmt.p_palette->i_entries; i++ )
701 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
702 fmt.p_palette->palette[i][1] = i_u;
703 fmt.p_palette->palette[i][2] = i_v;
704 fmt.p_palette->palette[i][3] = pi_gamma[i];
705 fmt.p_palette->palette[i][3] =
706 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
709 p_dst = p_region->picture.Y_PIXELS;
710 i_pitch = p_region->picture.Y_PITCH;
712 /* Initialize the region pixels */
713 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
715 for( ; p_line != NULL; p_line = p_line->p_next )
717 int i_glyph_tmax = 0;
718 int i_bitmap_offset, i_offset, i_align_offset = 0;
719 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
721 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
722 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
725 if( p_line->i_width < i_width )
727 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
729 i_align_offset = i_width - p_line->i_width;
731 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
733 i_align_offset = ( i_width - p_line->i_width ) / 2;
737 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
739 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
741 i_offset = ( p_line->p_glyph_pos[ i ].y +
742 i_glyph_tmax - p_glyph->top + 2 ) *
743 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
746 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
748 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
750 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
752 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
759 /* Outlining (find something better than nearest neighbour filtering ?) */
762 uint8_t *p_dst = p_region->picture.Y_PIXELS;
763 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
764 uint8_t left, current;
766 for( y = 1; y < (int)fmt.i_height - 1; y++ )
768 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
769 p_dst += p_region->picture.Y_PITCH;
772 for( x = 1; x < (int)fmt.i_width - 1; x++ )
775 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
776 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;
780 memset( p_top, 0, fmt.i_width );
786 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
787 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
788 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
789 int i_glyph_tmax, int i_align_offset,
790 uint8_t i_y, uint8_t i_u, uint8_t i_v,
791 subpicture_region_t *p_region)
795 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
797 p_dst_y = p_region->picture.Y_PIXELS;
798 p_dst_u = p_region->picture.U_PIXELS;
799 p_dst_v = p_region->picture.V_PIXELS;
800 p_dst_a = p_region->picture.A_PIXELS;
801 i_pitch = p_region->picture.A_PITCH;
803 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
804 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
806 for( y = 0; y < i_line_thickness; y++ )
808 int i_extra = p_this_glyph->bitmap.width;
812 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
813 (p_this_glyph_pos->x + p_this_glyph->left);
815 for( x = 0; x < i_extra; x++ )
819 /* break the underline around the tails of any glyphs which cross it */
820 for( z = x - i_line_thickness;
821 z < x + i_line_thickness && b_ok;
824 if( p_next_glyph && ( z >= i_extra ) )
826 int i_row = i_line_offset + p_next_glyph->top + y;
828 if( ( p_next_glyph->bitmap.rows > i_row ) &&
829 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
834 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
836 int i_row = i_line_offset + p_this_glyph->top + y;
838 if( ( p_this_glyph->bitmap.rows > i_row ) &&
839 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
848 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
849 p_dst_u[i_offset+x] = i_u;
850 p_dst_v[i_offset+x] = i_v;
851 p_dst_a[i_offset+x] = 255;
858 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
860 uint8_t *p_dst = p_region->picture.A_PIXELS;
861 int i_pitch = p_region->picture.A_PITCH;
864 for( ; p_line != NULL; p_line = p_line->p_next )
866 int i_glyph_tmax=0, i = 0;
867 int i_bitmap_offset, i_offset, i_align_offset = 0;
868 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
870 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
871 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
874 if( p_line->i_width < i_width )
876 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
878 i_align_offset = i_width - p_line->i_width;
880 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
882 i_align_offset = ( i_width - p_line->i_width ) / 2;
886 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
888 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
890 i_offset = ( p_line->p_glyph_pos[ i ].y +
891 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
892 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
893 i_align_offset +xoffset;
895 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
897 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
899 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
900 if( p_dst[i_offset+x] <
901 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
903 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
912 /*****************************************************************************
913 * Render: place string in picture
914 *****************************************************************************
915 * This function merges the previously rendered freetype glyphs into a picture
916 *****************************************************************************/
917 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
918 line_desc_t *p_line, int i_width, int i_height )
920 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
922 int i, x, y, i_pitch, i_alpha;
923 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
924 subpicture_region_t *p_region_tmp;
926 if( i_width == 0 || i_height == 0 )
929 /* Create a new subpicture region */
930 memset( &fmt, 0, sizeof(video_format_t) );
931 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
933 fmt.i_width = fmt.i_visible_width = i_width + 6;
934 fmt.i_height = fmt.i_visible_height = i_height + 6;
935 if( p_region->fmt.i_visible_width > 0 )
936 fmt.i_visible_width = p_region->fmt.i_visible_width;
937 if( p_region->fmt.i_visible_height > 0 )
938 fmt.i_visible_height = p_region->fmt.i_visible_height;
939 fmt.i_x_offset = fmt.i_y_offset = 0;
940 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
943 msg_Err( p_filter, "cannot allocate SPU region" );
947 p_region->fmt = p_region_tmp->fmt;
948 p_region->picture = p_region_tmp->picture;
949 free( p_region_tmp );
951 /* Calculate text color components */
952 YUVFromRGB( (p_line->i_red << 16) |
953 (p_line->i_green << 8) |
956 i_alpha = p_line->i_alpha;
958 p_dst_y = p_region->picture.Y_PIXELS;
959 p_dst_u = p_region->picture.U_PIXELS;
960 p_dst_v = p_region->picture.V_PIXELS;
961 p_dst_a = p_region->picture.A_PIXELS;
962 i_pitch = p_region->picture.A_PITCH;
964 /* Initialize the region pixels */
965 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
967 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
968 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
969 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
970 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
974 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
975 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
976 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
977 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
979 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
980 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
982 DrawBlack( p_line, i_width, p_region, 0, 0);
983 DrawBlack( p_line, i_width, p_region, -1, 0);
984 DrawBlack( p_line, i_width, p_region, 0, -1);
985 DrawBlack( p_line, i_width, p_region, 1, 0);
986 DrawBlack( p_line, i_width, p_region, 0, 1);
989 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
991 DrawBlack( p_line, i_width, p_region, -1, -1);
992 DrawBlack( p_line, i_width, p_region, -1, 1);
993 DrawBlack( p_line, i_width, p_region, 1, -1);
994 DrawBlack( p_line, i_width, p_region, 1, 1);
996 DrawBlack( p_line, i_width, p_region, -2, 0);
997 DrawBlack( p_line, i_width, p_region, 0, -2);
998 DrawBlack( p_line, i_width, p_region, 2, 0);
999 DrawBlack( p_line, i_width, p_region, 0, 2);
1001 DrawBlack( p_line, i_width, p_region, -2, -2);
1002 DrawBlack( p_line, i_width, p_region, -2, 2);
1003 DrawBlack( p_line, i_width, p_region, 2, -2);
1004 DrawBlack( p_line, i_width, p_region, 2, 2);
1006 DrawBlack( p_line, i_width, p_region, -3, 0);
1007 DrawBlack( p_line, i_width, p_region, 0, -3);
1008 DrawBlack( p_line, i_width, p_region, 3, 0);
1009 DrawBlack( p_line, i_width, p_region, 0, 3);
1012 for( ; p_line != NULL; p_line = p_line->p_next )
1014 int i_glyph_tmax = 0;
1015 int i_bitmap_offset, i_offset, i_align_offset = 0;
1016 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1018 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1019 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1022 if( p_line->i_width < i_width )
1024 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1026 i_align_offset = i_width - p_line->i_width;
1028 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1030 i_align_offset = ( i_width - p_line->i_width ) / 2;
1034 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1036 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1038 i_offset = ( p_line->p_glyph_pos[ i ].y +
1039 i_glyph_tmax - p_glyph->top + 3 ) *
1040 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1043 if( p_line->b_new_color_mode )
1045 /* Every glyph can (and in fact must) have its own color */
1046 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1049 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1051 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1053 uint8_t i_y_local = i_y;
1054 uint8_t i_u_local = i_u;
1055 uint8_t i_v_local = i_v;
1057 if( p_line->p_fg_bg_ratio != 0x00 )
1059 int i_split = p_glyph->bitmap.width *
1060 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1064 YUVFromRGB( p_line->p_bg_rgb[ i ],
1065 &i_y_local, &i_u_local, &i_v_local );
1069 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1071 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1072 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1074 p_dst_u[i_offset+x] = i_u;
1075 p_dst_v[i_offset+x] = i_v;
1077 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1078 p_dst_a[i_offset+x] = 0xff;
1081 i_offset += i_pitch;
1084 if( p_line->pi_underline_thickness[ i ] )
1086 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1087 p_line->pi_underline_offset[ i ],
1088 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1089 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1090 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1091 i_glyph_tmax, i_align_offset,
1098 /* Apply the alpha setting */
1099 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1100 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1106 * This function renders a text subpicture region into another one.
1107 * It also calculates the size needed for this string, and renders the
1108 * needed glyphs into memory. It is used as pf_add_string callback in
1109 * the vout method by this module
1111 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1112 subpicture_region_t *p_region_in )
1114 filter_sys_t *p_sys = p_filter->p_sys;
1115 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1116 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1117 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1118 int i_string_length;
1120 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1121 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1131 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1132 psz_string = p_region_in->psz_text;
1133 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1135 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1136 i_scale = val.i_int;
1138 if( p_region_in->p_style )
1140 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1141 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1142 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1146 i_font_color = p_sys->i_font_color;
1147 i_font_alpha = 255 - p_sys->i_font_opacity;
1148 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1151 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1152 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1153 SetFontSize( p_filter, i_font_size );
1155 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1156 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1157 i_blue = i_font_color & 0x000000FF;
1159 result.x = result.y = 0;
1160 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1162 psz_unicode = psz_unicode_orig =
1163 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1164 if( psz_unicode == NULL )
1166 #if defined(WORDS_BIGENDIAN)
1167 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1169 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1171 if( iconv_handle == (vlc_iconv_t)-1 )
1173 msg_Warn( p_filter, "unable to do conversion" );
1179 const char *p_in_buffer = psz_string;
1180 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1181 i_in_bytes = strlen( psz_string );
1182 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1183 i_out_bytes_left = i_out_bytes;
1184 p_out_buffer = (char *)psz_unicode;
1185 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1187 &p_out_buffer, &i_out_bytes_left );
1189 vlc_iconv_close( iconv_handle );
1193 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1194 "bytes left %u", (unsigned)i_in_bytes );
1197 *(uint32_t*)p_out_buffer = 0;
1198 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1201 #if defined(HAVE_FRIBIDI)
1203 uint32_t *p_fribidi_string;
1204 int32_t start_pos, pos = 0;
1206 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1207 if( !p_fribidi_string )
1210 /* Do bidi conversion line-by-line */
1211 while( pos < i_string_length )
1213 while( pos < i_string_length )
1215 i_char = psz_unicode[pos];
1216 if (i_char != '\r' && i_char != '\n')
1218 p_fribidi_string[pos] = i_char;
1222 while( pos < i_string_length )
1224 i_char = psz_unicode[pos];
1225 if (i_char == '\r' || i_char == '\n')
1229 if (pos > start_pos)
1231 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1232 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1235 (FriBidiChar*)p_fribidi_string + start_pos,
1240 free( psz_unicode_orig );
1241 psz_unicode = psz_unicode_orig = p_fribidi_string;
1242 p_fribidi_string[ i_string_length ] = 0;
1246 /* Calculate relative glyph positions and a bounding box for the
1248 if( !(p_line = NewLine( strlen( psz_string ))) )
1251 i_pen_x = i_pen_y = 0;
1253 psz_line_start = psz_unicode;
1255 #define face p_sys->p_face
1256 #define glyph face->glyph
1258 while( *psz_unicode )
1260 i_char = *psz_unicode++;
1261 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1266 if( i_char == '\n' )
1268 psz_line_start = psz_unicode;
1269 if( !(p_next = NewLine( strlen( psz_string ))) )
1271 p_line->p_next = p_next;
1272 p_line->i_width = line.xMax;
1273 p_line->i_height = face->size->metrics.height >> 6;
1274 p_line->pp_glyphs[ i ] = NULL;
1275 p_line->i_alpha = i_font_alpha;
1276 p_line->i_red = i_red;
1277 p_line->i_green = i_green;
1278 p_line->i_blue = i_blue;
1281 result.x = __MAX( result.x, line.xMax );
1282 result.y += face->size->metrics.height >> 6;
1285 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1286 i_pen_y += face->size->metrics.height >> 6;
1288 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1293 i_glyph_index = FT_Get_Char_Index( face, i_char );
1294 if( p_sys->i_use_kerning && i_glyph_index
1298 FT_Get_Kerning( face, i_previous, i_glyph_index,
1299 ft_kerning_default, &delta );
1300 i_pen_x += delta.x >> 6;
1303 p_line->p_glyph_pos[ i ].x = i_pen_x;
1304 p_line->p_glyph_pos[ i ].y = i_pen_y;
1305 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1308 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1312 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1315 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1319 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1320 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1323 FT_Done_Glyph( tmp_glyph );
1326 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1329 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1330 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1331 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1333 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1334 p_line->pp_glyphs[ i ] = NULL;
1336 p_line = NewLine( strlen( psz_string ));
1337 if( p_prev ) p_prev->p_next = p_line;
1338 else p_lines = p_line;
1340 uint32_t *psz_unicode_saved = psz_unicode;
1341 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1345 if( psz_unicode == psz_line_start )
1346 { /* try harder to break that line */
1347 psz_unicode = psz_unicode_saved;
1348 while( psz_unicode > psz_line_start &&
1349 *psz_unicode != '_' && *psz_unicode != '/' &&
1350 *psz_unicode != '\\' && *psz_unicode != '.' )
1355 if( psz_unicode == psz_line_start )
1357 msg_Warn( p_filter, "unbreakable string" );
1362 *psz_unicode = '\n';
1364 psz_unicode = psz_line_start;
1367 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1370 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1371 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1373 i_previous = i_glyph_index;
1374 i_pen_x += glyph->advance.x >> 6;
1378 p_line->i_width = line.xMax;
1379 p_line->i_height = face->size->metrics.height >> 6;
1380 p_line->pp_glyphs[ i ] = NULL;
1381 p_line->i_alpha = i_font_alpha;
1382 p_line->i_red = i_red;
1383 p_line->i_green = i_green;
1384 p_line->i_blue = i_blue;
1385 result.x = __MAX( result.x, line.xMax );
1386 result.y += line.yMax - line.yMin;
1391 p_region_out->i_x = p_region_in->i_x;
1392 p_region_out->i_y = p_region_in->i_y;
1394 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1395 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1397 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1399 free( psz_unicode_orig );
1400 FreeLines( p_lines );
1404 free( psz_unicode_orig );
1405 FreeLines( p_lines );
1406 return VLC_EGENERIC;
1409 #ifdef HAVE_FONTCONFIG
1410 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1411 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1412 bool b_italic, bool b_uline )
1414 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1418 p_style->i_font_size = i_font_size;
1419 p_style->i_font_color = i_font_color;
1420 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1421 p_style->b_italic = b_italic;
1422 p_style->b_bold = b_bold;
1423 p_style->b_underline = b_uline;
1425 p_style->psz_fontname = strdup( psz_fontname );
1430 static void DeleteStyle( ft_style_t *p_style )
1434 free( p_style->psz_fontname );
1439 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1446 if(( s1->i_font_size == s2->i_font_size ) &&
1447 ( s1->i_font_color == s2->i_font_color ) &&
1448 ( s1->b_italic == s2->b_italic ) &&
1449 ( s1->b_bold == s2->b_bold ) &&
1450 ( s1->b_underline == s2->b_underline ) &&
1451 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1458 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1459 uint32_t i_color, uint32_t i_karaoke_bg_color )
1461 font_stack_t *p_new;
1464 return VLC_EGENERIC;
1466 p_new = malloc( sizeof( font_stack_t ) );
1470 p_new->p_next = NULL;
1473 p_new->psz_name = strdup( psz_name );
1475 p_new->psz_name = NULL;
1477 p_new->i_size = i_size;
1478 p_new->i_color = i_color;
1479 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1487 font_stack_t *p_last;
1489 for( p_last = *p_font;
1491 p_last = p_last->p_next )
1494 p_last->p_next = p_new;
1499 static int PopFont( font_stack_t **p_font )
1501 font_stack_t *p_last, *p_next_to_last;
1503 if( !p_font || !*p_font )
1504 return VLC_EGENERIC;
1506 p_next_to_last = NULL;
1507 for( p_last = *p_font;
1509 p_last = p_last->p_next )
1511 p_next_to_last = p_last;
1514 if( p_next_to_last )
1515 p_next_to_last->p_next = NULL;
1519 free( p_last->psz_name );
1525 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1526 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1528 font_stack_t *p_last;
1530 if( !p_font || !*p_font )
1531 return VLC_EGENERIC;
1533 for( p_last=*p_font;
1535 p_last=p_last->p_next )
1538 *psz_name = p_last->psz_name;
1539 *i_size = p_last->i_size;
1540 *i_color = p_last->i_color;
1541 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1546 static void IconvText( filter_t *p_filter, const char *psz_string,
1547 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1549 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1551 /* If memory hasn't been allocated for our output string, allocate it here
1552 * - the calling function must now be responsible for freeing it.
1554 if( !*ppsz_unicode )
1555 *ppsz_unicode = (uint32_t *)
1556 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1558 /* We don't need to handle a NULL pointer in *ppsz_unicode
1559 * if we are instead testing for a non NULL value like we are here */
1563 #if defined(WORDS_BIGENDIAN)
1564 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1566 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1568 if( iconv_handle != (vlc_iconv_t)-1 )
1570 char *p_in_buffer, *p_out_buffer;
1571 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1572 i_in_bytes = strlen( psz_string );
1573 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1574 i_out_bytes_left = i_out_bytes;
1575 p_in_buffer = (char *) psz_string;
1576 p_out_buffer = (char *) *ppsz_unicode;
1577 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1578 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1580 vlc_iconv_close( iconv_handle );
1584 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1585 "bytes left %u", (unsigned)i_in_bytes );
1589 *(uint32_t*)p_out_buffer = 0;
1591 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1596 msg_Warn( p_filter, "unable to do conversion" );
1601 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1602 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1605 ft_style_t *p_style = NULL;
1607 char *psz_fontname = NULL;
1608 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1609 uint32_t i_karaoke_bg_color = i_font_color;
1610 int i_font_size = p_sys->i_font_size;
1612 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1613 &i_font_color, &i_karaoke_bg_color ))
1615 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1616 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1621 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1622 bool b_uline, int i_karaoke_bgcolor,
1623 line_desc_t *p_line, uint32_t *psz_unicode,
1624 int *pi_pen_x, int i_pen_y, int *pi_start,
1625 FT_Vector *p_result )
1630 bool b_first_on_line = true;
1633 int i_pen_x_start = *pi_pen_x;
1635 uint32_t *psz_unicode_start = psz_unicode;
1637 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1639 /* Account for part of line already in position */
1640 for( i=0; i<*pi_start; i++ )
1644 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1645 ft_glyph_bbox_pixels, &glyph_size );
1647 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1648 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1649 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1650 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1656 b_first_on_line = false;
1658 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1664 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1665 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1669 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1670 ft_kerning_default, &delta );
1671 *pi_pen_x += delta.x >> 6;
1673 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1674 p_line->p_glyph_pos[ i ].y = i_pen_y;
1676 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1680 "unable to render text FT_Load_Glyph returned %d", i_error );
1681 p_line->pp_glyphs[ i ] = NULL;
1682 return VLC_EGENERIC;
1684 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1688 "unable to render text FT_Get_Glyph returned %d", i_error );
1689 p_line->pp_glyphs[ i ] = NULL;
1690 return VLC_EGENERIC;
1692 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1693 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1696 FT_Done_Glyph( tmp_glyph );
1701 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1702 p_face->size->metrics.y_scale));
1703 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1704 p_face->size->metrics.y_scale));
1706 p_line->pi_underline_offset[ i ] =
1707 ( aOffset < 0 ) ? -aOffset : aOffset;
1708 p_line->pi_underline_thickness[ i ] =
1709 ( aSize < 0 ) ? -aSize : aSize;
1711 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1712 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1713 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1714 p_line->p_fg_bg_ratio[ i ] = 0x00;
1716 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1717 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1718 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1720 while( --i > *pi_start )
1722 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1725 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1729 if( psz_unicode == psz_unicode_start )
1731 if( b_first_on_line )
1733 msg_Warn( p_filter, "unbreakable string" );
1734 p_line->pp_glyphs[ i ] = NULL;
1735 return VLC_EGENERIC;
1737 *pi_pen_x = i_pen_x_start;
1739 p_line->i_width = line.xMax;
1740 p_line->i_height = __MAX( p_line->i_height,
1741 p_face->size->metrics.height >> 6 );
1742 p_line->pp_glyphs[ i ] = NULL;
1744 p_result->x = __MAX( p_result->x, line.xMax );
1745 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1746 i_yMax - i_yMin ) );
1753 *psz_unicode = '\n';
1755 psz_unicode = psz_unicode_start;
1756 *pi_pen_x = i_pen_x_start;
1764 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1765 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1767 i_previous = i_glyph_index;
1768 *pi_pen_x += p_face->glyph->advance.x >> 6;
1771 p_line->i_width = line.xMax;
1772 p_line->i_height = __MAX( p_line->i_height,
1773 p_face->size->metrics.height >> 6 );
1774 p_line->pp_glyphs[ i ] = NULL;
1776 p_result->x = __MAX( p_result->x, line.xMax );
1777 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1778 line.yMax - line.yMin ) );
1782 /* Get rid of any text processed - if necessary repositioning
1783 * at the start of a new line of text
1787 *psz_unicode_start = '\0';
1789 else if( psz_unicode > psz_unicode_start )
1791 for( i=0; psz_unicode[ i ]; i++ )
1792 psz_unicode_start[ i ] = psz_unicode[ i ];
1793 psz_unicode_start[ i ] = '\0';
1799 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1800 font_stack_t **p_fonts, int i_scale )
1803 char *psz_fontname = NULL;
1804 uint32_t i_font_color = 0xffffff;
1805 int i_font_alpha = 0;
1806 uint32_t i_karaoke_bg_color = 0x00ffffff;
1807 int i_font_size = 24;
1809 /* Default all attributes to the top font in the stack -- in case not
1810 * all attributes are specified in the sub-font
1812 if( VLC_SUCCESS == PeekFont( p_fonts,
1816 &i_karaoke_bg_color ))
1818 psz_fontname = strdup( psz_fontname );
1819 i_font_size = i_font_size * 1000 / i_scale;
1821 i_font_alpha = (i_font_color >> 24) & 0xff;
1822 i_font_color &= 0x00ffffff;
1824 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1826 char *psz_name = xml_ReaderName( p_xml_reader );
1827 char *psz_value = xml_ReaderValue( p_xml_reader );
1829 if( psz_name && psz_value )
1831 if( !strcasecmp( "face", psz_name ) )
1833 free( psz_fontname );
1834 psz_fontname = strdup( psz_value );
1836 else if( !strcasecmp( "size", psz_name ) )
1838 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1840 int i_value = atoi( psz_value );
1842 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1843 i_font_size += ( i_value * i_font_size ) / 10;
1844 else if( i_value < -5 )
1845 i_font_size = - i_value;
1846 else if( i_value > 5 )
1847 i_font_size = i_value;
1850 i_font_size = atoi( psz_value );
1852 else if( !strcasecmp( "color", psz_name ) &&
1853 ( psz_value[0] == '#' ) )
1855 i_font_color = strtol( psz_value + 1, NULL, 16 );
1856 i_font_color &= 0x00ffffff;
1858 else if( !strcasecmp( "alpha", psz_name ) &&
1859 ( psz_value[0] == '#' ) )
1861 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1862 i_font_alpha &= 0xff;
1868 rv = PushFont( p_fonts,
1870 i_font_size * i_scale / 1000,
1871 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1872 i_karaoke_bg_color );
1874 free( psz_fontname );
1879 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1880 uint32_t **psz_text_out, uint32_t *pi_runs,
1881 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1882 ft_style_t *p_style )
1884 uint32_t i_string_length = 0;
1886 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1887 *psz_text_out += i_string_length;
1889 if( ppp_styles && ppi_run_lengths )
1895 *ppp_styles = (ft_style_t **)
1896 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1898 else if( *pi_runs == 1 )
1900 *ppp_styles = (ft_style_t **)
1901 malloc( *pi_runs * sizeof( ft_style_t * ) );
1904 /* We have just malloc'ed this memory successfully -
1905 * *pi_runs HAS to be within the memory area of *ppp_styles */
1908 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1912 if( *ppi_run_lengths )
1914 *ppi_run_lengths = (uint32_t *)
1915 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1917 else if( *pi_runs == 1 )
1919 *ppi_run_lengths = (uint32_t *)
1920 malloc( *pi_runs * sizeof( uint32_t ) );
1923 /* same remarks here */
1924 if( *ppi_run_lengths )
1926 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1929 /* If we couldn't use the p_style argument due to memory allocation
1930 * problems above, release it here.
1932 if( p_style ) DeleteStyle( p_style );
1935 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1936 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1938 /* Karaoke tags _PRECEDE_ the text they specify a duration
1939 * for, therefore we are working out the length for the
1940 * previous tag, and first time through we have nothing
1942 if( pi_k_run_lengths )
1947 /* Work out how many characters are presently in the string
1949 for( i = 0; i < i_runs; i++ )
1950 i_chars += pi_run_lengths[ i ];
1952 /* Subtract away those we've already allocated to other
1955 for( i = 0; i < i_k_runs; i++ )
1956 i_chars -= pi_k_run_lengths[ i ];
1958 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1962 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1963 uint32_t **ppi_k_run_lengths,
1964 uint32_t **ppi_k_durations )
1966 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1968 char *psz_name = xml_ReaderName( p_xml_reader );
1969 char *psz_value = xml_ReaderValue( p_xml_reader );
1971 if( psz_name && psz_value &&
1972 !strcasecmp( "t", psz_name ) )
1974 if( ppi_k_durations && ppi_k_run_lengths )
1978 if( *ppi_k_durations )
1980 *ppi_k_durations = (uint32_t *)
1981 realloc( *ppi_k_durations,
1982 *pi_k_runs * sizeof( uint32_t ) );
1984 else if( *pi_k_runs == 1 )
1986 *ppi_k_durations = (uint32_t *)
1987 malloc( *pi_k_runs * sizeof( uint32_t ) );
1990 if( *ppi_k_run_lengths )
1992 *ppi_k_run_lengths = (uint32_t *)
1993 realloc( *ppi_k_run_lengths,
1994 *pi_k_runs * sizeof( uint32_t ) );
1996 else if( *pi_k_runs == 1 )
1998 *ppi_k_run_lengths = (uint32_t *)
1999 malloc( *pi_k_runs * sizeof( uint32_t ) );
2001 if( *ppi_k_durations )
2002 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
2004 if( *ppi_k_run_lengths )
2005 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
2013 static int ProcessNodes( filter_t *p_filter,
2014 xml_reader_t *p_xml_reader,
2015 text_style_t *p_font_style,
2020 uint32_t **ppi_run_lengths,
2021 ft_style_t ***ppp_styles,
2024 uint32_t *pi_k_runs,
2025 uint32_t **ppi_k_run_lengths,
2026 uint32_t **ppi_k_durations )
2028 int rv = VLC_SUCCESS;
2029 filter_sys_t *p_sys = p_filter->p_sys;
2030 uint32_t *psz_text_orig = psz_text;
2031 font_stack_t *p_fonts = NULL;
2035 char *psz_node = NULL;
2037 bool b_italic = false;
2038 bool b_bold = false;
2039 bool b_uline = false;
2041 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2042 i_scale = val.i_int;
2046 rv = PushFont( &p_fonts,
2047 p_font_style->psz_fontname,
2048 p_font_style->i_font_size * i_scale / 1000,
2049 (p_font_style->i_font_color & 0xffffff) |
2050 ((p_font_style->i_font_alpha & 0xff) << 24),
2051 (p_font_style->i_karaoke_background_color & 0xffffff) |
2052 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2054 if( p_font_style->i_style_flags & STYLE_BOLD )
2056 if( p_font_style->i_style_flags & STYLE_ITALIC )
2058 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2063 rv = PushFont( &p_fonts,
2069 if( rv != VLC_SUCCESS )
2072 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2074 switch ( xml_ReaderNodeType( p_xml_reader ) )
2076 case XML_READER_NONE:
2078 case XML_READER_ENDELEM:
2079 psz_node = xml_ReaderName( p_xml_reader );
2083 if( !strcasecmp( "font", psz_node ) )
2084 PopFont( &p_fonts );
2085 else if( !strcasecmp( "b", psz_node ) )
2087 else if( !strcasecmp( "i", psz_node ) )
2089 else if( !strcasecmp( "u", psz_node ) )
2095 case XML_READER_STARTELEM:
2096 psz_node = xml_ReaderName( p_xml_reader );
2099 if( !strcasecmp( "font", psz_node ) )
2100 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2101 else if( !strcasecmp( "b", psz_node ) )
2103 else if( !strcasecmp( "i", psz_node ) )
2105 else if( !strcasecmp( "u", psz_node ) )
2107 else if( !strcasecmp( "br", psz_node ) )
2109 SetupLine( p_filter, "\n", &psz_text,
2110 pi_runs, ppi_run_lengths, ppp_styles,
2111 GetStyleFromFontStack( p_sys,
2117 else if( !strcasecmp( "k", psz_node ) )
2119 /* Only valid in karaoke */
2122 if( *pi_k_runs > 0 )
2124 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2125 *pi_k_runs, *ppi_k_run_lengths );
2127 SetupKaraoke( p_xml_reader, pi_k_runs,
2128 ppi_k_run_lengths, ppi_k_durations );
2135 case XML_READER_TEXT:
2136 psz_node = xml_ReaderValue( p_xml_reader );
2139 /* Turn any multiple-whitespaces into single spaces */
2140 char *s = strpbrk( psz_node, "\t\r\n " );
2143 int i_whitespace = strspn( s, "\t\r\n " );
2145 if( i_whitespace > 1 )
2148 strlen( s ) - i_whitespace + 1 );
2151 s = strpbrk( s, "\t\r\n " );
2153 SetupLine( p_filter, psz_node, &psz_text,
2154 pi_runs, ppi_run_lengths, ppp_styles,
2155 GetStyleFromFontStack( p_sys,
2164 if( rv != VLC_SUCCESS )
2166 psz_text = psz_text_orig;
2172 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2173 *pi_k_runs, *ppi_k_run_lengths );
2176 *pi_len = psz_text - psz_text_orig;
2178 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2183 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2187 for( k=0; k < p_sys->i_font_attachments; k++ )
2189 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2191 FT_Face p_face = NULL;
2193 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2201 bool match = !strcasecmp( p_face->family_name,
2202 p_style->psz_fontname );
2204 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2205 match = match && p_style->b_bold;
2207 match = match && !p_style->b_bold;
2209 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2210 match = match && p_style->b_italic;
2212 match = match && !p_style->b_italic;
2220 FT_Done_Face( p_face );
2225 return VLC_EGENERIC;
2228 static int ProcessLines( filter_t *p_filter,
2233 uint32_t *pi_run_lengths,
2234 ft_style_t **pp_styles,
2235 line_desc_t **pp_lines,
2237 FT_Vector *p_result,
2241 uint32_t *pi_k_run_lengths,
2242 uint32_t *pi_k_durations )
2244 filter_sys_t *p_sys = p_filter->p_sys;
2245 ft_style_t **pp_char_styles;
2246 int *p_new_positions = NULL;
2247 int8_t *p_levels = NULL;
2248 uint8_t *pi_karaoke_bar = NULL;
2252 /* Assign each character in the text string its style explicitly, so that
2253 * after the characters have been shuffled around by Fribidi, we can re-apply
2254 * the styles, and to simplify the calculation of runs within a line.
2256 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2257 if( !pp_char_styles )
2262 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2263 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2264 * we just won't be able to display the progress bar; at least we'll
2270 for( j = 0; j < i_runs; j++ )
2271 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2272 pp_char_styles[ i++ ] = pp_styles[ j ];
2274 #if defined(HAVE_FRIBIDI)
2276 ft_style_t **pp_char_styles_new;
2277 int *p_old_positions;
2278 uint32_t *p_fribidi_string;
2279 int start_pos, pos = 0;
2281 pp_char_styles_new = (ft_style_t **)
2282 malloc( i_len * sizeof( ft_style_t * ));
2284 p_fribidi_string = (uint32_t *)
2285 malloc( (i_len + 1) * sizeof(uint32_t) );
2286 p_old_positions = (int *)
2287 malloc( (i_len + 1) * sizeof( int ) );
2288 p_new_positions = (int *)
2289 malloc( (i_len + 1) * sizeof( int ) );
2290 p_levels = (int8_t *)
2291 malloc( (i_len + 1) * sizeof( int8_t ) );
2293 if( ! pp_char_styles_new ||
2294 ! p_fribidi_string ||
2295 ! p_old_positions ||
2296 ! p_new_positions ||
2300 free( p_old_positions );
2301 free( p_new_positions );
2302 free( p_fribidi_string );
2303 free( pp_char_styles_new );
2304 free( pi_karaoke_bar );
2306 free( pp_char_styles );
2310 /* Do bidi conversion line-by-line */
2313 while(pos < i_len) {
2314 if (psz_text[pos] != '\n')
2316 p_fribidi_string[pos] = psz_text[pos];
2317 pp_char_styles_new[pos] = pp_char_styles[pos];
2318 p_new_positions[pos] = pos;
2323 while(pos < i_len) {
2324 if (psz_text[pos] == '\n')
2328 if (pos > start_pos)
2330 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2331 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2332 pos - start_pos, &base_dir,
2333 (FriBidiChar*)p_fribidi_string + start_pos,
2334 p_new_positions + start_pos,
2336 p_levels + start_pos );
2337 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2339 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2340 p_old_positions[ j - start_pos ] ];
2341 p_new_positions[ j ] += start_pos;
2345 free( p_old_positions );
2346 free( pp_char_styles );
2347 pp_char_styles = pp_char_styles_new;
2348 psz_text = p_fribidi_string;
2349 p_fribidi_string[ i_len ] = 0;
2352 /* Work out the karaoke */
2353 if( pi_karaoke_bar )
2355 int64_t i_last_duration = 0;
2356 int64_t i_duration = 0;
2357 int64_t i_start_pos = 0;
2358 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2360 for( k = 0; k< i_k_runs; k++ )
2362 double fraction = 0.0;
2364 i_duration += pi_k_durations[ k ];
2366 if( i_duration < i_elapsed )
2368 /* Completely finished this run-length -
2369 * let it render normally */
2373 else if( i_elapsed < i_last_duration )
2375 /* Haven't got up to this segment yet -
2376 * render it completely in karaoke BG mode */
2382 /* Partway through this run */
2384 fraction = (double)(i_elapsed - i_last_duration) /
2385 (double)pi_k_durations[ k ];
2387 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2389 double shade = pi_k_run_lengths[ k ] * fraction;
2391 if( p_new_positions )
2392 j = p_new_positions[ i_start_pos + i ];
2394 j = i_start_pos + i;
2396 if( i < (uint32_t)shade )
2397 pi_karaoke_bar[ j ] = 0xff;
2398 else if( (double)i > shade )
2399 pi_karaoke_bar[ j ] = 0x00;
2402 shade -= (int)shade;
2403 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2404 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2408 i_last_duration = i_duration;
2409 i_start_pos += pi_k_run_lengths[ k ];
2413 free( p_new_positions );
2415 FT_Vector tmp_result;
2417 line_desc_t *p_line = NULL;
2418 line_desc_t *p_prev = NULL;
2424 p_result->x = p_result->y = 0;
2425 tmp_result.x = tmp_result.y = 0;
2428 for( k = 0; k <= (uint32_t) i_len; k++ )
2430 if( ( k == (uint32_t) i_len ) ||
2432 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2434 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2436 /* End of the current style run */
2437 FT_Face p_face = NULL;
2440 /* Look for a match amongst our attachments first */
2441 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2445 char *psz_fontfile = NULL;
2447 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2448 if( p_sys->b_fontconfig_ok )
2450 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2451 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2452 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2453 p_style->psz_fontname,
2458 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2460 if( psz_fontfile && ! *psz_fontfile )
2462 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2463 " so using default font", p_style->psz_fontname,
2464 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2465 (p_style->b_bold ? "(Bold)" :
2466 (p_style->b_italic ? "(Italic)" : ""))) );
2467 free( psz_fontfile );
2468 psz_fontfile = NULL;
2473 if( FT_New_Face( p_sys->p_library,
2474 psz_fontfile, i_idx, &p_face ) )
2476 free( psz_fontfile );
2477 free( pp_char_styles );
2478 #if defined(HAVE_FRIBIDI)
2481 free( pi_karaoke_bar );
2482 return VLC_EGENERIC;
2484 free( psz_fontfile );
2488 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2490 /* We've loaded a font face which is unhelpful for actually
2491 * rendering text - fallback to the default one.
2493 FT_Done_Face( p_face );
2497 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2498 ft_encoding_unicode ) ||
2499 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2500 p_style->i_font_size ) )
2502 if( p_face ) FT_Done_Face( p_face );
2503 free( pp_char_styles );
2504 #if defined(HAVE_FRIBIDI)
2507 free( pi_karaoke_bar );
2508 return VLC_EGENERIC;
2510 p_sys->i_use_kerning =
2511 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2514 uint32_t *psz_unicode = (uint32_t *)
2515 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2518 if( p_face ) FT_Done_Face( p_face );
2519 free( pp_char_styles );
2520 free( psz_unicode );
2521 #if defined(HAVE_FRIBIDI)
2524 free( pi_karaoke_bar );
2527 memcpy( psz_unicode, psz_text + i_prev,
2528 sizeof( uint32_t ) * ( k - i_prev ) );
2529 psz_unicode[ k - i_prev ] = 0;
2530 while( *psz_unicode )
2534 if( !(p_line = NewLine( i_len - i_prev)) )
2536 if( p_face ) FT_Done_Face( p_face );
2537 free( pp_char_styles );
2538 free( psz_unicode );
2539 #if defined(HAVE_FRIBIDI)
2542 free( pi_karaoke_bar );
2545 /* New Color mode only works in YUVA rendering mode --
2546 * (RGB mode has palette constraints on it). We therefore
2547 * need to populate the legacy colour fields also.
2549 p_line->b_new_color_mode = true;
2550 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2551 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2552 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2553 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2554 p_line->p_next = NULL;
2556 i_pen_y += tmp_result.y;
2560 if( p_prev ) p_prev->p_next = p_line;
2561 else *pp_lines = p_line;
2564 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2565 p_style->i_font_color, p_style->b_underline,
2566 p_style->i_karaoke_bg_color,
2567 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2568 &tmp_result ) != VLC_SUCCESS )
2570 if( p_face ) FT_Done_Face( p_face );
2571 free( pp_char_styles );
2572 free( psz_unicode );
2573 #if defined(HAVE_FRIBIDI)
2576 free( pi_karaoke_bar );
2577 return VLC_EGENERIC;
2582 p_result->x = __MAX( p_result->x, tmp_result.x );
2583 p_result->y += tmp_result.y;
2588 if( *psz_unicode == '\n')
2592 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2594 *c_ptr = *(c_ptr+1);
2599 free( psz_unicode );
2600 if( p_face ) FT_Done_Face( p_face );
2604 free( pp_char_styles );
2605 #if defined(HAVE_FRIBIDI)
2610 p_result->x = __MAX( p_result->x, tmp_result.x );
2611 p_result->y += tmp_result.y;
2614 if( pi_karaoke_bar )
2617 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2619 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2621 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2625 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2627 /* 100% BG colour will render faster if we
2628 * instead make it 100% FG colour, so leave
2629 * the ratio alone and copy the value across
2631 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2635 if( pi_karaoke_bar[ i ] & 0x80 )
2637 /* Swap Left and Right sides over for Right aligned
2638 * language text (eg. Arabic, Hebrew)
2640 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2642 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2643 p_line->p_bg_rgb[ k ] = i_tmp;
2645 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2648 /* Jump over the '\n' at the line-end */
2651 free( pi_karaoke_bar );
2657 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2658 subpicture_region_t *p_region_in )
2660 int rv = VLC_SUCCESS;
2661 stream_t *p_sub = NULL;
2662 xml_t *p_xml = NULL;
2663 xml_reader_t *p_xml_reader = NULL;
2665 if( !p_region_in || !p_region_in->psz_html )
2666 return VLC_EGENERIC;
2668 /* Reset the default fontsize in case screen metrics have changed */
2669 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2671 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2672 (uint8_t *) p_region_in->psz_html,
2673 strlen( p_region_in->psz_html ),
2677 p_xml = xml_Create( p_filter );
2680 bool b_karaoke = false;
2682 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2685 /* Look for Root Node */
2686 if( xml_ReaderRead( p_xml_reader ) == 1 )
2688 char *psz_node = xml_ReaderName( p_xml_reader );
2690 if( !strcasecmp( "karaoke", psz_node ) )
2692 /* We're going to have to render the text a number
2693 * of times to show the progress marker on the text.
2695 var_SetBool( p_filter, "text-rerender", true );
2698 else if( !strcasecmp( "text", psz_node ) )
2704 /* Only text and karaoke tags are supported */
2705 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2706 xml_ReaderDelete( p_xml, p_xml_reader );
2707 p_xml_reader = NULL;
2719 uint32_t i_runs = 0;
2720 uint32_t i_k_runs = 0;
2721 uint32_t *pi_run_lengths = NULL;
2722 uint32_t *pi_k_run_lengths = NULL;
2723 uint32_t *pi_k_durations = NULL;
2724 ft_style_t **pp_styles = NULL;
2726 line_desc_t *p_lines = NULL;
2728 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2729 sizeof( uint32_t ) );
2734 rv = ProcessNodes( p_filter, p_xml_reader,
2735 p_region_in->p_style, psz_text, &i_len,
2736 &i_runs, &pi_run_lengths, &pp_styles,
2737 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2740 p_region_out->i_x = p_region_in->i_x;
2741 p_region_out->i_y = p_region_in->i_y;
2743 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2745 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2746 pi_run_lengths, pp_styles, &p_lines, &result,
2747 b_karaoke, i_k_runs, pi_k_run_lengths,
2751 for( k=0; k<i_runs; k++)
2752 DeleteStyle( pp_styles[k] );
2754 free( pi_run_lengths );
2757 /* Don't attempt to render text that couldn't be layed out
2760 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2762 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2764 Render( p_filter, p_region_out, p_lines,
2765 result.x, result.y );
2769 RenderYUVA( p_filter, p_region_out, p_lines,
2770 result.x, result.y );
2774 FreeLines( p_lines );
2776 xml_ReaderDelete( p_xml, p_xml_reader );
2778 xml_Delete( p_xml );
2780 stream_Delete( p_sub );
2786 static char* FontConfig_Select( FcConfig* priv, const char* family,
2787 bool b_bold, bool b_italic, int *i_idx )
2790 FcPattern *pat, *p_pat;
2794 pat = FcPatternCreate();
2795 if (!pat) return NULL;
2797 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2798 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2799 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2800 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2802 FcDefaultSubstitute( pat );
2804 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2806 FcPatternDestroy( pat );
2810 p_pat = FcFontMatch( priv, pat, &result );
2811 FcPatternDestroy( pat );
2812 if( !p_pat ) return NULL;
2814 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2815 || ( val_b != FcTrue ) )
2817 FcPatternDestroy( p_pat );
2820 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2825 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2827 FcPatternDestroy( p_pat );
2832 if( strcasecmp((const char*)val_s, family ) != 0 )
2833 msg_Warn( p_filter, "fontconfig: selected font family is not"
2834 "the requested one: '%s' != '%s'\n",
2835 (const char*)val_s, family );
2838 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2840 FcPatternDestroy( p_pat );
2844 FcPatternDestroy( p_pat );
2845 return strdup( (const char*)val_s );
2849 static void FreeLine( line_desc_t *p_line )
2852 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2854 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2856 free( p_line->pp_glyphs );
2857 free( p_line->p_glyph_pos );
2858 free( p_line->p_fg_rgb );
2859 free( p_line->p_bg_rgb );
2860 free( p_line->p_fg_bg_ratio );
2861 free( p_line->pi_underline_offset );
2862 free( p_line->pi_underline_thickness );
2866 static void FreeLines( line_desc_t *p_lines )
2868 line_desc_t *p_line, *p_next;
2870 if( !p_lines ) return;
2872 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2874 p_next = p_line->p_next;
2879 static line_desc_t *NewLine( int i_count )
2881 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2883 if( !p_line ) return NULL;
2884 p_line->i_height = 0;
2885 p_line->i_width = 0;
2886 p_line->p_next = NULL;
2888 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2889 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2890 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2891 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2892 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2893 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2894 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2895 if( ( p_line->pp_glyphs == NULL ) ||
2896 ( p_line->p_glyph_pos == NULL ) ||
2897 ( p_line->p_fg_rgb == NULL ) ||
2898 ( p_line->p_bg_rgb == NULL ) ||
2899 ( p_line->p_fg_bg_ratio == NULL ) ||
2900 ( p_line->pi_underline_offset == NULL ) ||
2901 ( p_line->pi_underline_thickness == NULL ) )
2903 free( p_line->pi_underline_thickness );
2904 free( p_line->pi_underline_offset );
2905 free( p_line->p_fg_rgb );
2906 free( p_line->p_bg_rgb );
2907 free( p_line->p_fg_bg_ratio );
2908 free( p_line->p_glyph_pos );
2909 free( p_line->pp_glyphs );
2913 p_line->pp_glyphs[0] = NULL;
2914 p_line->b_new_color_mode = false;
2919 static int GetFontSize( filter_t *p_filter )
2921 filter_sys_t *p_sys = p_filter->p_sys;
2925 if( p_sys->i_default_font_size )
2927 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2928 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2930 i_size = p_sys->i_default_font_size;
2934 var_Get( p_filter, "freetype-rel-fontsize", &val );
2935 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2936 p_filter->p_sys->i_display_height =
2937 p_filter->fmt_out.video.i_height;
2941 msg_Warn( p_filter, "invalid fontsize, using 12" );
2942 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2943 i_size = 12 * val.i_int / 1000;
2950 static int SetFontSize( filter_t *p_filter, int i_size )
2952 filter_sys_t *p_sys = p_filter->p_sys;
2956 i_size = GetFontSize( p_filter );
2958 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2961 p_sys->i_font_size = i_size;
2963 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2965 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2966 return VLC_EGENERIC;
2972 static void YUVFromRGB( uint32_t i_argb,
2973 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2975 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2976 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2977 int i_blue = ( i_argb & 0x000000ff );
2979 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2980 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2981 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2982 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2983 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2984 -585 * i_blue + 4096 + 1048576) >> 13, 240);