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 );
553 static void FontBuilderDestructor( vlc_object_t *p_this )
555 FcConfig *p_fontconfig = p_this->p_private;
558 FcConfigDestroy( p_fontconfig );
560 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
561 vlc_value_t oldval, vlc_value_t newval, void *param )
563 filter_t *p_filter = param;
564 filter_sys_t *p_sys = p_filter->p_sys;
568 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
570 p_sys->b_fontconfig_ok = true;
571 p_sys->p_fontconfig = p_this->p_private;
573 vlc_mutex_unlock( p_lock );
582 /*****************************************************************************
583 * Make any TTF/OTF fonts present in the attachments of the media file
584 * and store them for later use by the FreeType Engine
585 *****************************************************************************/
586 static int LoadFontsFromAttachments( filter_t *p_filter )
588 filter_sys_t *p_sys = p_filter->p_sys;
589 input_thread_t *p_input;
590 input_attachment_t **pp_attachments;
591 int i_attachments_cnt;
593 int rv = VLC_SUCCESS;
595 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
599 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
601 vlc_object_release(p_input);
605 p_sys->i_font_attachments = 0;
606 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
607 if(! p_sys->pp_font_attachments )
610 for( k = 0; k < i_attachments_cnt; k++ )
612 input_attachment_t *p_attach = pp_attachments[k];
614 if( p_sys->pp_font_attachments )
616 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
617 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
618 ( p_attach->i_data > 0 ) &&
619 ( p_attach->p_data != NULL ) )
621 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
625 vlc_input_attachment_Delete( p_attach );
630 vlc_input_attachment_Delete( p_attach );
633 free( pp_attachments );
635 vlc_object_release(p_input);
640 /*****************************************************************************
641 * Render: place string in picture
642 *****************************************************************************
643 * This function merges the previously rendered freetype glyphs into a picture
644 *****************************************************************************/
645 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
646 line_desc_t *p_line, int i_width, int i_height )
648 static const uint8_t pi_gamma[16] =
649 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
650 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
654 int i, x, y, i_pitch;
655 uint8_t i_y; /* YUV values, derived from incoming RGB */
657 subpicture_region_t *p_region_tmp;
659 /* Create a new subpicture region */
660 memset( &fmt, 0, sizeof(video_format_t) );
661 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
663 fmt.i_width = fmt.i_visible_width = i_width + 4;
664 fmt.i_height = fmt.i_visible_height = i_height + 4;
665 if( p_region->fmt.i_visible_width > 0 )
666 fmt.i_visible_width = p_region->fmt.i_visible_width;
667 if( p_region->fmt.i_visible_height > 0 )
668 fmt.i_visible_height = p_region->fmt.i_visible_height;
669 fmt.i_x_offset = fmt.i_y_offset = 0;
670 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
673 msg_Err( p_filter, "cannot allocate SPU region" );
677 p_region->fmt = p_region_tmp->fmt;
678 p_region->picture = p_region_tmp->picture;
679 free( p_region_tmp );
681 /* Calculate text color components */
682 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
683 25 * p_line->i_blue + 128) >> 8) + 16;
684 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
685 112 * p_line->i_blue + 128) >> 8) + 128;
686 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
687 18 * p_line->i_blue + 128) >> 8) + 128;
690 fmt.p_palette->i_entries = 16;
691 for( i = 0; i < 8; i++ )
693 fmt.p_palette->palette[i][0] = 0;
694 fmt.p_palette->palette[i][1] = 0x80;
695 fmt.p_palette->palette[i][2] = 0x80;
696 fmt.p_palette->palette[i][3] = pi_gamma[i];
697 fmt.p_palette->palette[i][3] =
698 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
700 for( i = 8; i < fmt.p_palette->i_entries; i++ )
702 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
703 fmt.p_palette->palette[i][1] = i_u;
704 fmt.p_palette->palette[i][2] = i_v;
705 fmt.p_palette->palette[i][3] = pi_gamma[i];
706 fmt.p_palette->palette[i][3] =
707 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
710 p_dst = p_region->picture.Y_PIXELS;
711 i_pitch = p_region->picture.Y_PITCH;
713 /* Initialize the region pixels */
714 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
716 for( ; p_line != NULL; p_line = p_line->p_next )
718 int i_glyph_tmax = 0;
719 int i_bitmap_offset, i_offset, i_align_offset = 0;
720 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
722 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
723 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
726 if( p_line->i_width < i_width )
728 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
730 i_align_offset = i_width - p_line->i_width;
732 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
734 i_align_offset = ( i_width - p_line->i_width ) / 2;
738 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
740 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
742 i_offset = ( p_line->p_glyph_pos[ i ].y +
743 i_glyph_tmax - p_glyph->top + 2 ) *
744 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
747 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
749 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
751 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
753 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
760 /* Outlining (find something better than nearest neighbour filtering ?) */
763 uint8_t *p_dst = p_region->picture.Y_PIXELS;
764 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
765 uint8_t left, current;
767 for( y = 1; y < (int)fmt.i_height - 1; y++ )
769 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
770 p_dst += p_region->picture.Y_PITCH;
773 for( x = 1; x < (int)fmt.i_width - 1; x++ )
776 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
777 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;
781 memset( p_top, 0, fmt.i_width );
787 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
788 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
789 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
790 int i_glyph_tmax, int i_align_offset,
791 uint8_t i_y, uint8_t i_u, uint8_t i_v,
792 subpicture_region_t *p_region)
796 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
798 p_dst_y = p_region->picture.Y_PIXELS;
799 p_dst_u = p_region->picture.U_PIXELS;
800 p_dst_v = p_region->picture.V_PIXELS;
801 p_dst_a = p_region->picture.A_PIXELS;
802 i_pitch = p_region->picture.A_PITCH;
804 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
805 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
807 for( y = 0; y < i_line_thickness; y++ )
809 int i_extra = p_this_glyph->bitmap.width;
813 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
814 (p_this_glyph_pos->x + p_this_glyph->left);
816 for( x = 0; x < i_extra; x++ )
820 /* break the underline around the tails of any glyphs which cross it */
821 for( z = x - i_line_thickness;
822 z < x + i_line_thickness && b_ok;
825 if( p_next_glyph && ( z >= i_extra ) )
827 int i_row = i_line_offset + p_next_glyph->top + y;
829 if( ( p_next_glyph->bitmap.rows > i_row ) &&
830 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
835 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
837 int i_row = i_line_offset + p_this_glyph->top + y;
839 if( ( p_this_glyph->bitmap.rows > i_row ) &&
840 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
849 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
850 p_dst_u[i_offset+x] = i_u;
851 p_dst_v[i_offset+x] = i_v;
852 p_dst_a[i_offset+x] = 255;
859 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
861 uint8_t *p_dst = p_region->picture.A_PIXELS;
862 int i_pitch = p_region->picture.A_PITCH;
865 for( ; p_line != NULL; p_line = p_line->p_next )
867 int i_glyph_tmax=0, i = 0;
868 int i_bitmap_offset, i_offset, i_align_offset = 0;
869 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
871 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
872 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
875 if( p_line->i_width < i_width )
877 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
879 i_align_offset = i_width - p_line->i_width;
881 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
883 i_align_offset = ( i_width - p_line->i_width ) / 2;
887 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
889 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
891 i_offset = ( p_line->p_glyph_pos[ i ].y +
892 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
893 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
894 i_align_offset +xoffset;
896 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
898 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
900 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
901 if( p_dst[i_offset+x] <
902 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
904 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
913 /*****************************************************************************
914 * Render: place string in picture
915 *****************************************************************************
916 * This function merges the previously rendered freetype glyphs into a picture
917 *****************************************************************************/
918 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
919 line_desc_t *p_line, int i_width, int i_height )
921 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
923 int i, x, y, i_pitch, i_alpha;
924 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
925 subpicture_region_t *p_region_tmp;
927 if( i_width == 0 || i_height == 0 )
930 /* Create a new subpicture region */
931 memset( &fmt, 0, sizeof(video_format_t) );
932 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
934 fmt.i_width = fmt.i_visible_width = i_width + 6;
935 fmt.i_height = fmt.i_visible_height = i_height + 6;
936 if( p_region->fmt.i_visible_width > 0 )
937 fmt.i_visible_width = p_region->fmt.i_visible_width;
938 if( p_region->fmt.i_visible_height > 0 )
939 fmt.i_visible_height = p_region->fmt.i_visible_height;
940 fmt.i_x_offset = fmt.i_y_offset = 0;
941 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
944 msg_Err( p_filter, "cannot allocate SPU region" );
948 p_region->fmt = p_region_tmp->fmt;
949 p_region->picture = p_region_tmp->picture;
950 free( p_region_tmp );
952 /* Calculate text color components */
953 YUVFromRGB( (p_line->i_red << 16) |
954 (p_line->i_green << 8) |
957 i_alpha = p_line->i_alpha;
959 p_dst_y = p_region->picture.Y_PIXELS;
960 p_dst_u = p_region->picture.U_PIXELS;
961 p_dst_v = p_region->picture.V_PIXELS;
962 p_dst_a = p_region->picture.A_PIXELS;
963 i_pitch = p_region->picture.A_PITCH;
965 /* Initialize the region pixels */
966 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
968 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
969 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
970 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
971 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
975 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
976 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
977 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
978 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
980 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
981 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
983 DrawBlack( p_line, i_width, p_region, 0, 0);
984 DrawBlack( p_line, i_width, p_region, -1, 0);
985 DrawBlack( p_line, i_width, p_region, 0, -1);
986 DrawBlack( p_line, i_width, p_region, 1, 0);
987 DrawBlack( p_line, i_width, p_region, 0, 1);
990 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
995 DrawBlack( p_line, i_width, p_region, 1, 1);
997 DrawBlack( p_line, i_width, p_region, -2, 0);
998 DrawBlack( p_line, i_width, p_region, 0, -2);
999 DrawBlack( p_line, i_width, p_region, 2, 0);
1000 DrawBlack( p_line, i_width, p_region, 0, 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);
1005 DrawBlack( p_line, i_width, p_region, 2, 2);
1007 DrawBlack( p_line, i_width, p_region, -3, 0);
1008 DrawBlack( p_line, i_width, p_region, 0, -3);
1009 DrawBlack( p_line, i_width, p_region, 3, 0);
1010 DrawBlack( p_line, i_width, p_region, 0, 3);
1013 for( ; p_line != NULL; p_line = p_line->p_next )
1015 int i_glyph_tmax = 0;
1016 int i_bitmap_offset, i_offset, i_align_offset = 0;
1017 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1019 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1020 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1023 if( p_line->i_width < i_width )
1025 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1027 i_align_offset = i_width - p_line->i_width;
1029 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1031 i_align_offset = ( i_width - p_line->i_width ) / 2;
1035 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1037 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1039 i_offset = ( p_line->p_glyph_pos[ i ].y +
1040 i_glyph_tmax - p_glyph->top + 3 ) *
1041 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1044 if( p_line->b_new_color_mode )
1046 /* Every glyph can (and in fact must) have its own color */
1047 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1050 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1052 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1054 uint8_t i_y_local = i_y;
1055 uint8_t i_u_local = i_u;
1056 uint8_t i_v_local = i_v;
1058 if( p_line->p_fg_bg_ratio != 0x00 )
1060 int i_split = p_glyph->bitmap.width *
1061 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1065 YUVFromRGB( p_line->p_bg_rgb[ i ],
1066 &i_y_local, &i_u_local, &i_v_local );
1070 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1072 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1073 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1075 p_dst_u[i_offset+x] = i_u;
1076 p_dst_v[i_offset+x] = i_v;
1078 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1079 p_dst_a[i_offset+x] = 0xff;
1082 i_offset += i_pitch;
1085 if( p_line->pi_underline_thickness[ i ] )
1087 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1088 p_line->pi_underline_offset[ i ],
1089 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1090 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1091 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1092 i_glyph_tmax, i_align_offset,
1099 /* Apply the alpha setting */
1100 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1101 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1107 * This function renders a text subpicture region into another one.
1108 * It also calculates the size needed for this string, and renders the
1109 * needed glyphs into memory. It is used as pf_add_string callback in
1110 * the vout method by this module
1112 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1113 subpicture_region_t *p_region_in )
1115 filter_sys_t *p_sys = p_filter->p_sys;
1116 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1117 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1118 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1119 int i_string_length;
1121 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1122 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1132 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1133 psz_string = p_region_in->psz_text;
1134 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1136 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1137 i_scale = val.i_int;
1139 if( p_region_in->p_style )
1141 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1142 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1143 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1147 i_font_color = p_sys->i_font_color;
1148 i_font_alpha = 255 - p_sys->i_font_opacity;
1149 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1152 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1153 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1154 SetFontSize( p_filter, i_font_size );
1156 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1157 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1158 i_blue = i_font_color & 0x000000FF;
1160 result.x = result.y = 0;
1161 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1163 psz_unicode = psz_unicode_orig =
1164 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1165 if( psz_unicode == NULL )
1167 #if defined(WORDS_BIGENDIAN)
1168 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1170 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1172 if( iconv_handle == (vlc_iconv_t)-1 )
1174 msg_Warn( p_filter, "unable to do conversion" );
1180 const char *p_in_buffer = psz_string;
1181 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1182 i_in_bytes = strlen( psz_string );
1183 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1184 i_out_bytes_left = i_out_bytes;
1185 p_out_buffer = (char *)psz_unicode;
1186 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1188 &p_out_buffer, &i_out_bytes_left );
1190 vlc_iconv_close( iconv_handle );
1194 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1195 "bytes left %u", (unsigned)i_in_bytes );
1198 *(uint32_t*)p_out_buffer = 0;
1199 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1202 #if defined(HAVE_FRIBIDI)
1204 uint32_t *p_fribidi_string;
1205 int32_t start_pos, pos = 0;
1207 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1208 if( !p_fribidi_string )
1211 /* Do bidi conversion line-by-line */
1212 while( pos < i_string_length )
1214 while( pos < i_string_length )
1216 i_char = psz_unicode[pos];
1217 if (i_char != '\r' && i_char != '\n')
1219 p_fribidi_string[pos] = i_char;
1223 while( pos < i_string_length )
1225 i_char = psz_unicode[pos];
1226 if (i_char == '\r' || i_char == '\n')
1230 if (pos > start_pos)
1232 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1233 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1236 (FriBidiChar*)p_fribidi_string + start_pos,
1241 free( psz_unicode_orig );
1242 psz_unicode = psz_unicode_orig = p_fribidi_string;
1243 p_fribidi_string[ i_string_length ] = 0;
1247 /* Calculate relative glyph positions and a bounding box for the
1249 if( !(p_line = NewLine( strlen( psz_string ))) )
1252 i_pen_x = i_pen_y = 0;
1254 psz_line_start = psz_unicode;
1256 #define face p_sys->p_face
1257 #define glyph face->glyph
1259 while( *psz_unicode )
1261 i_char = *psz_unicode++;
1262 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1267 if( i_char == '\n' )
1269 psz_line_start = psz_unicode;
1270 if( !(p_next = NewLine( strlen( psz_string ))) )
1272 p_line->p_next = p_next;
1273 p_line->i_width = line.xMax;
1274 p_line->i_height = face->size->metrics.height >> 6;
1275 p_line->pp_glyphs[ i ] = NULL;
1276 p_line->i_alpha = i_font_alpha;
1277 p_line->i_red = i_red;
1278 p_line->i_green = i_green;
1279 p_line->i_blue = i_blue;
1282 result.x = __MAX( result.x, line.xMax );
1283 result.y += face->size->metrics.height >> 6;
1286 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1287 i_pen_y += face->size->metrics.height >> 6;
1289 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1294 i_glyph_index = FT_Get_Char_Index( face, i_char );
1295 if( p_sys->i_use_kerning && i_glyph_index
1299 FT_Get_Kerning( face, i_previous, i_glyph_index,
1300 ft_kerning_default, &delta );
1301 i_pen_x += delta.x >> 6;
1304 p_line->p_glyph_pos[ i ].x = i_pen_x;
1305 p_line->p_glyph_pos[ i ].y = i_pen_y;
1306 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1309 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1313 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1316 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1320 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1321 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1324 FT_Done_Glyph( tmp_glyph );
1327 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1330 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1331 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1332 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1334 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1335 p_line->pp_glyphs[ i ] = NULL;
1337 p_line = NewLine( strlen( psz_string ));
1338 if( p_prev ) p_prev->p_next = p_line;
1339 else p_lines = p_line;
1341 uint32_t *psz_unicode_saved = psz_unicode;
1342 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1346 if( psz_unicode == psz_line_start )
1347 { /* try harder to break that line */
1348 psz_unicode = psz_unicode_saved;
1349 while( psz_unicode > psz_line_start &&
1350 *psz_unicode != '_' && *psz_unicode != '/' &&
1351 *psz_unicode != '\\' && *psz_unicode != '.' )
1356 if( psz_unicode == psz_line_start )
1358 msg_Warn( p_filter, "unbreakable string" );
1363 *psz_unicode = '\n';
1365 psz_unicode = psz_line_start;
1368 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1371 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1372 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1374 i_previous = i_glyph_index;
1375 i_pen_x += glyph->advance.x >> 6;
1379 p_line->i_width = line.xMax;
1380 p_line->i_height = face->size->metrics.height >> 6;
1381 p_line->pp_glyphs[ i ] = NULL;
1382 p_line->i_alpha = i_font_alpha;
1383 p_line->i_red = i_red;
1384 p_line->i_green = i_green;
1385 p_line->i_blue = i_blue;
1386 result.x = __MAX( result.x, line.xMax );
1387 result.y += line.yMax - line.yMin;
1392 p_region_out->i_x = p_region_in->i_x;
1393 p_region_out->i_y = p_region_in->i_y;
1395 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1396 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1398 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1400 free( psz_unicode_orig );
1401 FreeLines( p_lines );
1405 free( psz_unicode_orig );
1406 FreeLines( p_lines );
1407 return VLC_EGENERIC;
1410 #ifdef HAVE_FONTCONFIG
1411 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1412 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1413 bool b_italic, bool b_uline )
1415 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1419 p_style->i_font_size = i_font_size;
1420 p_style->i_font_color = i_font_color;
1421 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1422 p_style->b_italic = b_italic;
1423 p_style->b_bold = b_bold;
1424 p_style->b_underline = b_uline;
1426 p_style->psz_fontname = strdup( psz_fontname );
1431 static void DeleteStyle( ft_style_t *p_style )
1435 free( p_style->psz_fontname );
1440 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1447 if(( s1->i_font_size == s2->i_font_size ) &&
1448 ( s1->i_font_color == s2->i_font_color ) &&
1449 ( s1->b_italic == s2->b_italic ) &&
1450 ( s1->b_bold == s2->b_bold ) &&
1451 ( s1->b_underline == s2->b_underline ) &&
1452 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1459 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1460 uint32_t i_color, uint32_t i_karaoke_bg_color )
1462 font_stack_t *p_new;
1465 return VLC_EGENERIC;
1467 p_new = malloc( sizeof( font_stack_t ) );
1471 p_new->p_next = NULL;
1474 p_new->psz_name = strdup( psz_name );
1476 p_new->psz_name = NULL;
1478 p_new->i_size = i_size;
1479 p_new->i_color = i_color;
1480 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1488 font_stack_t *p_last;
1490 for( p_last = *p_font;
1492 p_last = p_last->p_next )
1495 p_last->p_next = p_new;
1500 static int PopFont( font_stack_t **p_font )
1502 font_stack_t *p_last, *p_next_to_last;
1504 if( !p_font || !*p_font )
1505 return VLC_EGENERIC;
1507 p_next_to_last = NULL;
1508 for( p_last = *p_font;
1510 p_last = p_last->p_next )
1512 p_next_to_last = p_last;
1515 if( p_next_to_last )
1516 p_next_to_last->p_next = NULL;
1520 free( p_last->psz_name );
1526 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1527 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1529 font_stack_t *p_last;
1531 if( !p_font || !*p_font )
1532 return VLC_EGENERIC;
1534 for( p_last=*p_font;
1536 p_last=p_last->p_next )
1539 *psz_name = p_last->psz_name;
1540 *i_size = p_last->i_size;
1541 *i_color = p_last->i_color;
1542 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1547 static void IconvText( filter_t *p_filter, const char *psz_string,
1548 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1550 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1552 /* If memory hasn't been allocated for our output string, allocate it here
1553 * - the calling function must now be responsible for freeing it.
1555 if( !*ppsz_unicode )
1556 *ppsz_unicode = (uint32_t *)
1557 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1559 /* We don't need to handle a NULL pointer in *ppsz_unicode
1560 * if we are instead testing for a non NULL value like we are here */
1564 #if defined(WORDS_BIGENDIAN)
1565 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1567 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1569 if( iconv_handle != (vlc_iconv_t)-1 )
1571 char *p_in_buffer, *p_out_buffer;
1572 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1573 i_in_bytes = strlen( psz_string );
1574 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1575 i_out_bytes_left = i_out_bytes;
1576 p_in_buffer = (char *) psz_string;
1577 p_out_buffer = (char *) *ppsz_unicode;
1578 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1579 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1581 vlc_iconv_close( iconv_handle );
1585 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1586 "bytes left %u", (unsigned)i_in_bytes );
1590 *(uint32_t*)p_out_buffer = 0;
1592 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1597 msg_Warn( p_filter, "unable to do conversion" );
1602 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1603 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1606 ft_style_t *p_style = NULL;
1608 char *psz_fontname = NULL;
1609 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1610 uint32_t i_karaoke_bg_color = i_font_color;
1611 int i_font_size = p_sys->i_font_size;
1613 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1614 &i_font_color, &i_karaoke_bg_color ))
1616 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1617 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1622 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1623 bool b_uline, int i_karaoke_bgcolor,
1624 line_desc_t *p_line, uint32_t *psz_unicode,
1625 int *pi_pen_x, int i_pen_y, int *pi_start,
1626 FT_Vector *p_result )
1631 bool b_first_on_line = true;
1634 int i_pen_x_start = *pi_pen_x;
1636 uint32_t *psz_unicode_start = psz_unicode;
1638 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1640 /* Account for part of line already in position */
1641 for( i=0; i<*pi_start; i++ )
1645 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1646 ft_glyph_bbox_pixels, &glyph_size );
1648 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1649 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1650 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1651 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1657 b_first_on_line = false;
1659 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1665 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1666 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1670 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1671 ft_kerning_default, &delta );
1672 *pi_pen_x += delta.x >> 6;
1674 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1675 p_line->p_glyph_pos[ i ].y = i_pen_y;
1677 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1681 "unable to render text FT_Load_Glyph returned %d", i_error );
1682 p_line->pp_glyphs[ i ] = NULL;
1683 return VLC_EGENERIC;
1685 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1689 "unable to render text FT_Get_Glyph returned %d", i_error );
1690 p_line->pp_glyphs[ i ] = NULL;
1691 return VLC_EGENERIC;
1693 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1694 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1697 FT_Done_Glyph( tmp_glyph );
1702 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1703 p_face->size->metrics.y_scale));
1704 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1705 p_face->size->metrics.y_scale));
1707 p_line->pi_underline_offset[ i ] =
1708 ( aOffset < 0 ) ? -aOffset : aOffset;
1709 p_line->pi_underline_thickness[ i ] =
1710 ( aSize < 0 ) ? -aSize : aSize;
1712 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1713 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1714 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1715 p_line->p_fg_bg_ratio[ i ] = 0x00;
1717 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1718 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1719 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1721 while( --i > *pi_start )
1723 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1726 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1730 if( psz_unicode == psz_unicode_start )
1732 if( b_first_on_line )
1734 msg_Warn( p_filter, "unbreakable string" );
1735 p_line->pp_glyphs[ i ] = NULL;
1736 return VLC_EGENERIC;
1738 *pi_pen_x = i_pen_x_start;
1740 p_line->i_width = line.xMax;
1741 p_line->i_height = __MAX( p_line->i_height,
1742 p_face->size->metrics.height >> 6 );
1743 p_line->pp_glyphs[ i ] = NULL;
1745 p_result->x = __MAX( p_result->x, line.xMax );
1746 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1747 i_yMax - i_yMin ) );
1754 *psz_unicode = '\n';
1756 psz_unicode = psz_unicode_start;
1757 *pi_pen_x = i_pen_x_start;
1765 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1766 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1768 i_previous = i_glyph_index;
1769 *pi_pen_x += p_face->glyph->advance.x >> 6;
1772 p_line->i_width = line.xMax;
1773 p_line->i_height = __MAX( p_line->i_height,
1774 p_face->size->metrics.height >> 6 );
1775 p_line->pp_glyphs[ i ] = NULL;
1777 p_result->x = __MAX( p_result->x, line.xMax );
1778 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1779 line.yMax - line.yMin ) );
1783 /* Get rid of any text processed - if necessary repositioning
1784 * at the start of a new line of text
1788 *psz_unicode_start = '\0';
1790 else if( psz_unicode > psz_unicode_start )
1792 for( i=0; psz_unicode[ i ]; i++ )
1793 psz_unicode_start[ i ] = psz_unicode[ i ];
1794 psz_unicode_start[ i ] = '\0';
1800 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1801 font_stack_t **p_fonts, int i_scale )
1804 char *psz_fontname = NULL;
1805 uint32_t i_font_color = 0xffffff;
1806 int i_font_alpha = 0;
1807 uint32_t i_karaoke_bg_color = 0x00ffffff;
1808 int i_font_size = 24;
1810 /* Default all attributes to the top font in the stack -- in case not
1811 * all attributes are specified in the sub-font
1813 if( VLC_SUCCESS == PeekFont( p_fonts,
1817 &i_karaoke_bg_color ))
1819 psz_fontname = strdup( psz_fontname );
1820 i_font_size = i_font_size * 1000 / i_scale;
1822 i_font_alpha = (i_font_color >> 24) & 0xff;
1823 i_font_color &= 0x00ffffff;
1825 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1827 char *psz_name = xml_ReaderName( p_xml_reader );
1828 char *psz_value = xml_ReaderValue( p_xml_reader );
1830 if( psz_name && psz_value )
1832 if( !strcasecmp( "face", psz_name ) )
1834 free( psz_fontname );
1835 psz_fontname = strdup( psz_value );
1837 else if( !strcasecmp( "size", psz_name ) )
1839 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1841 int i_value = atoi( psz_value );
1843 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1844 i_font_size += ( i_value * i_font_size ) / 10;
1845 else if( i_value < -5 )
1846 i_font_size = - i_value;
1847 else if( i_value > 5 )
1848 i_font_size = i_value;
1851 i_font_size = atoi( psz_value );
1853 else if( !strcasecmp( "color", psz_name ) &&
1854 ( psz_value[0] == '#' ) )
1856 i_font_color = strtol( psz_value + 1, NULL, 16 );
1857 i_font_color &= 0x00ffffff;
1859 else if( !strcasecmp( "alpha", psz_name ) &&
1860 ( psz_value[0] == '#' ) )
1862 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1863 i_font_alpha &= 0xff;
1869 rv = PushFont( p_fonts,
1871 i_font_size * i_scale / 1000,
1872 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1873 i_karaoke_bg_color );
1875 free( psz_fontname );
1880 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1881 uint32_t **psz_text_out, uint32_t *pi_runs,
1882 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1883 ft_style_t *p_style )
1885 uint32_t i_string_length = 0;
1887 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1888 *psz_text_out += i_string_length;
1890 if( ppp_styles && ppi_run_lengths )
1896 *ppp_styles = (ft_style_t **)
1897 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1899 else if( *pi_runs == 1 )
1901 *ppp_styles = (ft_style_t **)
1902 malloc( *pi_runs * sizeof( ft_style_t * ) );
1905 /* We have just malloc'ed this memory successfully -
1906 * *pi_runs HAS to be within the memory area of *ppp_styles */
1909 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1913 if( *ppi_run_lengths )
1915 *ppi_run_lengths = (uint32_t *)
1916 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1918 else if( *pi_runs == 1 )
1920 *ppi_run_lengths = (uint32_t *)
1921 malloc( *pi_runs * sizeof( uint32_t ) );
1924 /* same remarks here */
1925 if( *ppi_run_lengths )
1927 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1930 /* If we couldn't use the p_style argument due to memory allocation
1931 * problems above, release it here.
1933 if( p_style ) DeleteStyle( p_style );
1936 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1937 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1939 /* Karaoke tags _PRECEDE_ the text they specify a duration
1940 * for, therefore we are working out the length for the
1941 * previous tag, and first time through we have nothing
1943 if( pi_k_run_lengths )
1948 /* Work out how many characters are presently in the string
1950 for( i = 0; i < i_runs; i++ )
1951 i_chars += pi_run_lengths[ i ];
1953 /* Subtract away those we've already allocated to other
1956 for( i = 0; i < i_k_runs; i++ )
1957 i_chars -= pi_k_run_lengths[ i ];
1959 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1963 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1964 uint32_t **ppi_k_run_lengths,
1965 uint32_t **ppi_k_durations )
1967 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1969 char *psz_name = xml_ReaderName( p_xml_reader );
1970 char *psz_value = xml_ReaderValue( p_xml_reader );
1972 if( psz_name && psz_value &&
1973 !strcasecmp( "t", psz_name ) )
1975 if( ppi_k_durations && ppi_k_run_lengths )
1979 if( *ppi_k_durations )
1981 *ppi_k_durations = (uint32_t *)
1982 realloc( *ppi_k_durations,
1983 *pi_k_runs * sizeof( uint32_t ) );
1985 else if( *pi_k_runs == 1 )
1987 *ppi_k_durations = (uint32_t *)
1988 malloc( *pi_k_runs * sizeof( uint32_t ) );
1991 if( *ppi_k_run_lengths )
1993 *ppi_k_run_lengths = (uint32_t *)
1994 realloc( *ppi_k_run_lengths,
1995 *pi_k_runs * sizeof( uint32_t ) );
1997 else if( *pi_k_runs == 1 )
1999 *ppi_k_run_lengths = (uint32_t *)
2000 malloc( *pi_k_runs * sizeof( uint32_t ) );
2002 if( *ppi_k_durations )
2003 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
2005 if( *ppi_k_run_lengths )
2006 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
2014 static int ProcessNodes( filter_t *p_filter,
2015 xml_reader_t *p_xml_reader,
2016 text_style_t *p_font_style,
2021 uint32_t **ppi_run_lengths,
2022 ft_style_t ***ppp_styles,
2025 uint32_t *pi_k_runs,
2026 uint32_t **ppi_k_run_lengths,
2027 uint32_t **ppi_k_durations )
2029 int rv = VLC_SUCCESS;
2030 filter_sys_t *p_sys = p_filter->p_sys;
2031 uint32_t *psz_text_orig = psz_text;
2032 font_stack_t *p_fonts = NULL;
2036 char *psz_node = NULL;
2038 bool b_italic = false;
2039 bool b_bold = false;
2040 bool b_uline = false;
2042 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2043 i_scale = val.i_int;
2047 rv = PushFont( &p_fonts,
2048 p_font_style->psz_fontname,
2049 p_font_style->i_font_size * i_scale / 1000,
2050 (p_font_style->i_font_color & 0xffffff) |
2051 ((p_font_style->i_font_alpha & 0xff) << 24),
2052 (p_font_style->i_karaoke_background_color & 0xffffff) |
2053 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2055 if( p_font_style->i_style_flags & STYLE_BOLD )
2057 if( p_font_style->i_style_flags & STYLE_ITALIC )
2059 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2064 rv = PushFont( &p_fonts,
2070 if( rv != VLC_SUCCESS )
2073 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2075 switch ( xml_ReaderNodeType( p_xml_reader ) )
2077 case XML_READER_NONE:
2079 case XML_READER_ENDELEM:
2080 psz_node = xml_ReaderName( p_xml_reader );
2084 if( !strcasecmp( "font", psz_node ) )
2085 PopFont( &p_fonts );
2086 else if( !strcasecmp( "b", psz_node ) )
2088 else if( !strcasecmp( "i", psz_node ) )
2090 else if( !strcasecmp( "u", psz_node ) )
2096 case XML_READER_STARTELEM:
2097 psz_node = xml_ReaderName( p_xml_reader );
2100 if( !strcasecmp( "font", psz_node ) )
2101 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2102 else if( !strcasecmp( "b", psz_node ) )
2104 else if( !strcasecmp( "i", psz_node ) )
2106 else if( !strcasecmp( "u", psz_node ) )
2108 else if( !strcasecmp( "br", psz_node ) )
2110 SetupLine( p_filter, "\n", &psz_text,
2111 pi_runs, ppi_run_lengths, ppp_styles,
2112 GetStyleFromFontStack( p_sys,
2118 else if( !strcasecmp( "k", psz_node ) )
2120 /* Only valid in karaoke */
2123 if( *pi_k_runs > 0 )
2125 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2126 *pi_k_runs, *ppi_k_run_lengths );
2128 SetupKaraoke( p_xml_reader, pi_k_runs,
2129 ppi_k_run_lengths, ppi_k_durations );
2136 case XML_READER_TEXT:
2137 psz_node = xml_ReaderValue( p_xml_reader );
2140 /* Turn any multiple-whitespaces into single spaces */
2141 char *s = strpbrk( psz_node, "\t\r\n " );
2144 int i_whitespace = strspn( s, "\t\r\n " );
2146 if( i_whitespace > 1 )
2149 strlen( s ) - i_whitespace + 1 );
2152 s = strpbrk( s, "\t\r\n " );
2154 SetupLine( p_filter, psz_node, &psz_text,
2155 pi_runs, ppi_run_lengths, ppp_styles,
2156 GetStyleFromFontStack( p_sys,
2165 if( rv != VLC_SUCCESS )
2167 psz_text = psz_text_orig;
2173 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2174 *pi_k_runs, *ppi_k_run_lengths );
2177 *pi_len = psz_text - psz_text_orig;
2179 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2184 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2188 for( k=0; k < p_sys->i_font_attachments; k++ )
2190 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2192 FT_Face p_face = NULL;
2194 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2202 bool match = !strcasecmp( p_face->family_name,
2203 p_style->psz_fontname );
2205 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2206 match = match && p_style->b_bold;
2208 match = match && !p_style->b_bold;
2210 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2211 match = match && p_style->b_italic;
2213 match = match && !p_style->b_italic;
2221 FT_Done_Face( p_face );
2226 return VLC_EGENERIC;
2229 static int ProcessLines( filter_t *p_filter,
2234 uint32_t *pi_run_lengths,
2235 ft_style_t **pp_styles,
2236 line_desc_t **pp_lines,
2238 FT_Vector *p_result,
2242 uint32_t *pi_k_run_lengths,
2243 uint32_t *pi_k_durations )
2245 filter_sys_t *p_sys = p_filter->p_sys;
2246 ft_style_t **pp_char_styles;
2247 int *p_new_positions = NULL;
2248 int8_t *p_levels = NULL;
2249 uint8_t *pi_karaoke_bar = NULL;
2253 /* Assign each character in the text string its style explicitly, so that
2254 * after the characters have been shuffled around by Fribidi, we can re-apply
2255 * the styles, and to simplify the calculation of runs within a line.
2257 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2258 if( !pp_char_styles )
2263 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2264 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2265 * we just won't be able to display the progress bar; at least we'll
2271 for( j = 0; j < i_runs; j++ )
2272 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2273 pp_char_styles[ i++ ] = pp_styles[ j ];
2275 #if defined(HAVE_FRIBIDI)
2277 ft_style_t **pp_char_styles_new;
2278 int *p_old_positions;
2279 uint32_t *p_fribidi_string;
2280 int start_pos, pos = 0;
2282 pp_char_styles_new = (ft_style_t **)
2283 malloc( i_len * sizeof( ft_style_t * ));
2285 p_fribidi_string = (uint32_t *)
2286 malloc( (i_len + 1) * sizeof(uint32_t) );
2287 p_old_positions = (int *)
2288 malloc( (i_len + 1) * sizeof( int ) );
2289 p_new_positions = (int *)
2290 malloc( (i_len + 1) * sizeof( int ) );
2291 p_levels = (int8_t *)
2292 malloc( (i_len + 1) * sizeof( int8_t ) );
2294 if( ! pp_char_styles_new ||
2295 ! p_fribidi_string ||
2296 ! p_old_positions ||
2297 ! p_new_positions ||
2301 free( p_old_positions );
2302 free( p_new_positions );
2303 free( p_fribidi_string );
2304 free( pp_char_styles_new );
2305 free( pi_karaoke_bar );
2307 free( pp_char_styles );
2311 /* Do bidi conversion line-by-line */
2314 while(pos < i_len) {
2315 if (psz_text[pos] != '\n')
2317 p_fribidi_string[pos] = psz_text[pos];
2318 pp_char_styles_new[pos] = pp_char_styles[pos];
2319 p_new_positions[pos] = pos;
2324 while(pos < i_len) {
2325 if (psz_text[pos] == '\n')
2329 if (pos > start_pos)
2331 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2332 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2333 pos - start_pos, &base_dir,
2334 (FriBidiChar*)p_fribidi_string + start_pos,
2335 p_new_positions + start_pos,
2337 p_levels + start_pos );
2338 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2340 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2341 p_old_positions[ j - start_pos ] ];
2342 p_new_positions[ j ] += start_pos;
2346 free( p_old_positions );
2347 free( pp_char_styles );
2348 pp_char_styles = pp_char_styles_new;
2349 psz_text = p_fribidi_string;
2350 p_fribidi_string[ i_len ] = 0;
2353 /* Work out the karaoke */
2354 if( pi_karaoke_bar )
2356 int64_t i_last_duration = 0;
2357 int64_t i_duration = 0;
2358 int64_t i_start_pos = 0;
2359 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2361 for( k = 0; k< i_k_runs; k++ )
2363 double fraction = 0.0;
2365 i_duration += pi_k_durations[ k ];
2367 if( i_duration < i_elapsed )
2369 /* Completely finished this run-length -
2370 * let it render normally */
2374 else if( i_elapsed < i_last_duration )
2376 /* Haven't got up to this segment yet -
2377 * render it completely in karaoke BG mode */
2383 /* Partway through this run */
2385 fraction = (double)(i_elapsed - i_last_duration) /
2386 (double)pi_k_durations[ k ];
2388 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2390 double shade = pi_k_run_lengths[ k ] * fraction;
2392 if( p_new_positions )
2393 j = p_new_positions[ i_start_pos + i ];
2395 j = i_start_pos + i;
2397 if( i < (uint32_t)shade )
2398 pi_karaoke_bar[ j ] = 0xff;
2399 else if( (double)i > shade )
2400 pi_karaoke_bar[ j ] = 0x00;
2403 shade -= (int)shade;
2404 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2405 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2409 i_last_duration = i_duration;
2410 i_start_pos += pi_k_run_lengths[ k ];
2414 free( p_new_positions );
2416 FT_Vector tmp_result;
2418 line_desc_t *p_line = NULL;
2419 line_desc_t *p_prev = NULL;
2425 p_result->x = p_result->y = 0;
2426 tmp_result.x = tmp_result.y = 0;
2429 for( k = 0; k <= (uint32_t) i_len; k++ )
2431 if( ( k == (uint32_t) i_len ) ||
2433 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2435 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2437 /* End of the current style run */
2438 FT_Face p_face = NULL;
2441 /* Look for a match amongst our attachments first */
2442 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2446 char *psz_fontfile = NULL;
2448 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2449 if( p_sys->b_fontconfig_ok )
2451 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2452 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2453 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2454 p_style->psz_fontname,
2459 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2461 if( psz_fontfile && ! *psz_fontfile )
2463 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2464 " so using default font", p_style->psz_fontname,
2465 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2466 (p_style->b_bold ? "(Bold)" :
2467 (p_style->b_italic ? "(Italic)" : ""))) );
2468 free( psz_fontfile );
2469 psz_fontfile = NULL;
2474 if( FT_New_Face( p_sys->p_library,
2475 psz_fontfile, i_idx, &p_face ) )
2477 free( psz_fontfile );
2478 free( pp_char_styles );
2479 #if defined(HAVE_FRIBIDI)
2482 free( pi_karaoke_bar );
2483 return VLC_EGENERIC;
2485 free( psz_fontfile );
2489 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2491 /* We've loaded a font face which is unhelpful for actually
2492 * rendering text - fallback to the default one.
2494 FT_Done_Face( p_face );
2498 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2499 ft_encoding_unicode ) ||
2500 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2501 p_style->i_font_size ) )
2503 if( p_face ) FT_Done_Face( p_face );
2504 free( pp_char_styles );
2505 #if defined(HAVE_FRIBIDI)
2508 free( pi_karaoke_bar );
2509 return VLC_EGENERIC;
2511 p_sys->i_use_kerning =
2512 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2515 uint32_t *psz_unicode = (uint32_t *)
2516 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2519 if( p_face ) FT_Done_Face( p_face );
2520 free( pp_char_styles );
2521 free( psz_unicode );
2522 #if defined(HAVE_FRIBIDI)
2525 free( pi_karaoke_bar );
2528 memcpy( psz_unicode, psz_text + i_prev,
2529 sizeof( uint32_t ) * ( k - i_prev ) );
2530 psz_unicode[ k - i_prev ] = 0;
2531 while( *psz_unicode )
2535 if( !(p_line = NewLine( i_len - i_prev)) )
2537 if( p_face ) FT_Done_Face( p_face );
2538 free( pp_char_styles );
2539 free( psz_unicode );
2540 #if defined(HAVE_FRIBIDI)
2543 free( pi_karaoke_bar );
2546 /* New Color mode only works in YUVA rendering mode --
2547 * (RGB mode has palette constraints on it). We therefore
2548 * need to populate the legacy colour fields also.
2550 p_line->b_new_color_mode = true;
2551 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2552 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2553 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2554 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2555 p_line->p_next = NULL;
2557 i_pen_y += tmp_result.y;
2561 if( p_prev ) p_prev->p_next = p_line;
2562 else *pp_lines = p_line;
2565 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2566 p_style->i_font_color, p_style->b_underline,
2567 p_style->i_karaoke_bg_color,
2568 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2569 &tmp_result ) != VLC_SUCCESS )
2571 if( p_face ) FT_Done_Face( p_face );
2572 free( pp_char_styles );
2573 free( psz_unicode );
2574 #if defined(HAVE_FRIBIDI)
2577 free( pi_karaoke_bar );
2578 return VLC_EGENERIC;
2583 p_result->x = __MAX( p_result->x, tmp_result.x );
2584 p_result->y += tmp_result.y;
2589 if( *psz_unicode == '\n')
2593 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2595 *c_ptr = *(c_ptr+1);
2600 free( psz_unicode );
2601 if( p_face ) FT_Done_Face( p_face );
2605 free( pp_char_styles );
2606 #if defined(HAVE_FRIBIDI)
2611 p_result->x = __MAX( p_result->x, tmp_result.x );
2612 p_result->y += tmp_result.y;
2615 if( pi_karaoke_bar )
2618 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2620 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2622 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2626 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2628 /* 100% BG colour will render faster if we
2629 * instead make it 100% FG colour, so leave
2630 * the ratio alone and copy the value across
2632 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2636 if( pi_karaoke_bar[ i ] & 0x80 )
2638 /* Swap Left and Right sides over for Right aligned
2639 * language text (eg. Arabic, Hebrew)
2641 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2643 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2644 p_line->p_bg_rgb[ k ] = i_tmp;
2646 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2649 /* Jump over the '\n' at the line-end */
2652 free( pi_karaoke_bar );
2658 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2659 subpicture_region_t *p_region_in )
2661 int rv = VLC_SUCCESS;
2662 stream_t *p_sub = NULL;
2663 xml_t *p_xml = NULL;
2664 xml_reader_t *p_xml_reader = NULL;
2666 if( !p_region_in || !p_region_in->psz_html )
2667 return VLC_EGENERIC;
2669 /* Reset the default fontsize in case screen metrics have changed */
2670 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2672 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2673 (uint8_t *) p_region_in->psz_html,
2674 strlen( p_region_in->psz_html ),
2678 p_xml = xml_Create( p_filter );
2681 bool b_karaoke = false;
2683 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2686 /* Look for Root Node */
2687 if( xml_ReaderRead( p_xml_reader ) == 1 )
2689 char *psz_node = xml_ReaderName( p_xml_reader );
2691 if( !strcasecmp( "karaoke", psz_node ) )
2693 /* We're going to have to render the text a number
2694 * of times to show the progress marker on the text.
2696 var_SetBool( p_filter, "text-rerender", true );
2699 else if( !strcasecmp( "text", psz_node ) )
2705 /* Only text and karaoke tags are supported */
2706 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2707 xml_ReaderDelete( p_xml, p_xml_reader );
2708 p_xml_reader = NULL;
2720 uint32_t i_runs = 0;
2721 uint32_t i_k_runs = 0;
2722 uint32_t *pi_run_lengths = NULL;
2723 uint32_t *pi_k_run_lengths = NULL;
2724 uint32_t *pi_k_durations = NULL;
2725 ft_style_t **pp_styles = NULL;
2727 line_desc_t *p_lines = NULL;
2729 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2730 sizeof( uint32_t ) );
2735 rv = ProcessNodes( p_filter, p_xml_reader,
2736 p_region_in->p_style, psz_text, &i_len,
2737 &i_runs, &pi_run_lengths, &pp_styles,
2738 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2741 p_region_out->i_x = p_region_in->i_x;
2742 p_region_out->i_y = p_region_in->i_y;
2744 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2746 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2747 pi_run_lengths, pp_styles, &p_lines, &result,
2748 b_karaoke, i_k_runs, pi_k_run_lengths,
2752 for( k=0; k<i_runs; k++)
2753 DeleteStyle( pp_styles[k] );
2755 free( pi_run_lengths );
2758 /* Don't attempt to render text that couldn't be layed out
2761 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2763 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2765 Render( p_filter, p_region_out, p_lines,
2766 result.x, result.y );
2770 RenderYUVA( p_filter, p_region_out, p_lines,
2771 result.x, result.y );
2775 FreeLines( p_lines );
2777 xml_ReaderDelete( p_xml, p_xml_reader );
2779 xml_Delete( p_xml );
2781 stream_Delete( p_sub );
2787 static char* FontConfig_Select( FcConfig* priv, const char* family,
2788 bool b_bold, bool b_italic, int *i_idx )
2791 FcPattern *pat, *p_pat;
2795 pat = FcPatternCreate();
2796 if (!pat) return NULL;
2798 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2799 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2800 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2801 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2803 FcDefaultSubstitute( pat );
2805 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2807 FcPatternDestroy( pat );
2811 p_pat = FcFontMatch( priv, pat, &result );
2812 FcPatternDestroy( pat );
2813 if( !p_pat ) return NULL;
2815 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2816 || ( val_b != FcTrue ) )
2818 FcPatternDestroy( p_pat );
2821 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2826 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2828 FcPatternDestroy( p_pat );
2833 if( strcasecmp((const char*)val_s, family ) != 0 )
2834 msg_Warn( p_filter, "fontconfig: selected font family is not"
2835 "the requested one: '%s' != '%s'\n",
2836 (const char*)val_s, family );
2839 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2841 FcPatternDestroy( p_pat );
2845 FcPatternDestroy( p_pat );
2846 return strdup( (const char*)val_s );
2850 static void FreeLine( line_desc_t *p_line )
2853 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2855 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2857 free( p_line->pp_glyphs );
2858 free( p_line->p_glyph_pos );
2859 free( p_line->p_fg_rgb );
2860 free( p_line->p_bg_rgb );
2861 free( p_line->p_fg_bg_ratio );
2862 free( p_line->pi_underline_offset );
2863 free( p_line->pi_underline_thickness );
2867 static void FreeLines( line_desc_t *p_lines )
2869 line_desc_t *p_line, *p_next;
2871 if( !p_lines ) return;
2873 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2875 p_next = p_line->p_next;
2880 static line_desc_t *NewLine( int i_count )
2882 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2884 if( !p_line ) return NULL;
2885 p_line->i_height = 0;
2886 p_line->i_width = 0;
2887 p_line->p_next = NULL;
2889 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2890 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2891 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2892 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2893 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2894 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2895 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2896 if( ( p_line->pp_glyphs == NULL ) ||
2897 ( p_line->p_glyph_pos == NULL ) ||
2898 ( p_line->p_fg_rgb == NULL ) ||
2899 ( p_line->p_bg_rgb == NULL ) ||
2900 ( p_line->p_fg_bg_ratio == NULL ) ||
2901 ( p_line->pi_underline_offset == NULL ) ||
2902 ( p_line->pi_underline_thickness == NULL ) )
2904 free( p_line->pi_underline_thickness );
2905 free( p_line->pi_underline_offset );
2906 free( p_line->p_fg_rgb );
2907 free( p_line->p_bg_rgb );
2908 free( p_line->p_fg_bg_ratio );
2909 free( p_line->p_glyph_pos );
2910 free( p_line->pp_glyphs );
2914 p_line->pp_glyphs[0] = NULL;
2915 p_line->b_new_color_mode = false;
2920 static int GetFontSize( filter_t *p_filter )
2922 filter_sys_t *p_sys = p_filter->p_sys;
2926 if( p_sys->i_default_font_size )
2928 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2929 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2931 i_size = p_sys->i_default_font_size;
2935 var_Get( p_filter, "freetype-rel-fontsize", &val );
2936 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2937 p_filter->p_sys->i_display_height =
2938 p_filter->fmt_out.video.i_height;
2942 msg_Warn( p_filter, "invalid fontsize, using 12" );
2943 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2944 i_size = 12 * val.i_int / 1000;
2951 static int SetFontSize( filter_t *p_filter, int i_size )
2953 filter_sys_t *p_sys = p_filter->p_sys;
2957 i_size = GetFontSize( p_filter );
2959 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2962 p_sys->i_font_size = i_size;
2964 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2966 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2967 return VLC_EGENERIC;
2973 static void YUVFromRGB( uint32_t i_argb,
2974 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2976 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2977 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2978 int i_blue = ( i_argb & 0x000000ff );
2980 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2981 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2982 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2983 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2984 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2985 -585 * i_blue + 4096 + 1048576) >> 13, 240);