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>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
47 #include FT_FREETYPE_H
49 #define FT_FLOOR(X) ((X & -64) >> 6)
50 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
51 #define FT_MulFix(v, s) (((v)*(s))>>16)
54 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
55 #define FC_DEFAULT_FONT "Lucida Grande"
56 #elif defined( SYS_BEOS )
57 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
58 #define FC_DEFAULT_FONT "Swiss"
59 #elif defined( WIN32 )
60 #define DEFAULT_FONT "" /* Default font found at run-time */
61 #define FC_DEFAULT_FONT "Arial"
63 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
64 #define FC_DEFAULT_FONT "Serif Bold"
67 #if defined(HAVE_FRIBIDI)
68 #include <fribidi/fribidi.h>
71 #ifdef HAVE_FONTCONFIG
72 #include <fontconfig/fontconfig.h>
77 /*****************************************************************************
79 *****************************************************************************/
80 static int Create ( vlc_object_t * );
81 static void Destroy( vlc_object_t * );
83 #define FONT_TEXT N_("Font")
84 #define FONT_LONGTEXT N_("Filename for the font you want to use")
85 #define FONTSIZE_TEXT N_("Font size in pixels")
86 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
87 "that will be rendered on the video. " \
88 "If set to something different than 0 this option will override the " \
89 "relative font size." )
90 #define OPACITY_TEXT N_("Opacity")
91 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
92 "text that will be rendered on the video. 0 = transparent, " \
93 "255 = totally opaque. " )
94 #define COLOR_TEXT N_("Text default color")
95 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
96 "the video. This must be an hexadecimal (like HTML colors). The first two "\
97 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
98 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
99 #define FONTSIZER_TEXT N_("Relative font size")
100 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
101 "fonts that will be rendered on the video. If absolute font size is set, "\
102 "relative size will be overriden." )
104 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
105 static const char *const ppsz_sizes_text[] = {
106 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
107 #define YUVP_TEXT N_("Use YUVP renderer")
108 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
109 "This option is only needed if you want to encode into DVB subtitles" )
110 #define EFFECT_TEXT N_("Font Effect")
111 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
112 "text to improve its readability." )
114 #define EFFECT_BACKGROUND 1
115 #define EFFECT_OUTLINE 2
116 #define EFFECT_OUTLINE_FAT 3
118 static int const pi_effects[] = { 1, 2, 3 };
119 static const char *const ppsz_effects_text[] = {
120 N_("Background"),N_("Outline"), N_("Fat Outline") };
121 static const int pi_color_values[] = {
122 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
123 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
124 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
126 static const char *const ppsz_color_descriptions[] = {
127 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
128 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
129 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
132 set_shortname( N_("Text renderer"))
133 set_description( N_("Freetype2 font renderer") )
134 set_category( CAT_VIDEO )
135 set_subcategory( SUBCAT_VIDEO_SUBPIC )
137 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
140 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
141 FONTSIZE_LONGTEXT, true )
143 /* opacity valid on 0..255, with default 255 = fully opaque */
144 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
145 OPACITY_TEXT, OPACITY_LONGTEXT, true )
147 /* hook to the color values list, with default 0x00ffffff = white */
148 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
149 COLOR_LONGTEXT, false )
150 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
152 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
153 FONTSIZER_LONGTEXT, false )
154 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
155 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
156 EFFECT_LONGTEXT, false )
157 change_integer_list( pi_effects, ppsz_effects_text, NULL )
159 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
160 YUVP_LONGTEXT, true )
161 set_capability( "text renderer", 100 )
162 add_shortcut( "text" )
163 set_callbacks( Create, Destroy )
168 /*****************************************************************************
170 *****************************************************************************/
172 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
173 static int RenderText( filter_t *, subpicture_region_t *,
174 subpicture_region_t * );
175 #ifdef HAVE_FONTCONFIG
176 static int RenderHtml( filter_t *, subpicture_region_t *,
177 subpicture_region_t * );
178 static char *FontConfig_Select( FcConfig *, const char *,
183 static int LoadFontsFromAttachments( filter_t *p_filter );
185 static int GetFontSize( filter_t *p_filter );
186 static int SetFontSize( filter_t *, int );
187 static void YUVFromRGB( uint32_t i_argb,
188 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
190 typedef struct line_desc_t line_desc_t;
193 /** NULL-terminated list of glyphs making the string */
194 FT_BitmapGlyph *pp_glyphs;
195 /** list of relative positions for the glyphs */
196 FT_Vector *p_glyph_pos;
197 /** list of RGB information for styled text
198 * -- if the rendering mode supports it (RenderYUVA) and
199 * b_new_color_mode is set, then it becomes possible to
200 * have multicoloured text within the subtitles. */
203 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
204 bool b_new_color_mode;
205 /** underline information -- only supplied if text should be underlined */
206 uint16_t *pi_underline_offset;
207 uint16_t *pi_underline_thickness;
211 int i_red, i_green, i_blue;
216 static line_desc_t *NewLine( int );
221 uint32_t i_font_color; /* ARGB */
222 uint32_t i_karaoke_bg_color; /* ARGB */
229 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
230 static void FreeLines( line_desc_t * );
231 static void FreeLine( line_desc_t * );
233 #ifdef HAVE_FONTCONFIG
234 static vlc_object_t *FontBuilderAttach( filter_t *p_filter );
235 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
236 static void* FontBuilderThread( vlc_object_t *p_this);
237 static void FontBuilderDestructor( vlc_object_t *p_this );
238 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
239 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
243 /*****************************************************************************
244 * filter_sys_t: freetype local data
245 *****************************************************************************
246 * This structure is part of the video output thread descriptor.
247 * It describes the freetype specific properties of an output thread.
248 *****************************************************************************/
251 FT_Library p_library; /* handle to library */
252 FT_Face p_face; /* handle to face object */
254 uint8_t i_font_opacity;
259 int i_default_font_size;
260 int i_display_height;
261 #ifdef HAVE_FONTCONFIG
262 bool b_fontconfig_ok;
263 FcConfig *p_fontconfig;
266 input_attachment_t **pp_font_attachments;
267 int i_font_attachments;
269 vlc_object_t *p_fontbuilder;
272 #define UCHAR uint32_t
273 #define TR_DEFAULT_FONT FC_DEFAULT_FONT
274 #define TR_FONT_STYLE_PTR ft_style_t *
276 #include "text_renderer.h"
278 /*****************************************************************************
279 * Create: allocates osd-text video thread output method
280 *****************************************************************************
281 * This function allocates and initializes a Clone vout method.
282 *****************************************************************************/
283 static int Create( vlc_object_t *p_this )
285 filter_t *p_filter = (filter_t *)p_this;
287 char *psz_fontfile = NULL;
291 /* Allocate structure */
292 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
296 p_sys->p_library = 0;
297 p_sys->i_font_size = 0;
298 p_sys->i_display_height = 0;
300 var_Create( p_filter, "freetype-font",
301 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
302 var_Create( p_filter, "freetype-fontsize",
303 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
304 var_Create( p_filter, "freetype-rel-fontsize",
305 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
306 var_Create( p_filter, "freetype-opacity",
307 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
308 var_Create( p_filter, "freetype-effect",
309 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
310 var_Get( p_filter, "freetype-opacity", &val );
311 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
312 var_Create( p_filter, "freetype-color",
313 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
314 var_Get( p_filter, "freetype-color", &val );
315 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
316 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
318 /* Look what method was requested */
319 var_Get( p_filter, "freetype-font", &val );
320 psz_fontfile = val.psz_string;
321 if( !psz_fontfile || !*psz_fontfile )
323 free( psz_fontfile );
324 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
328 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
329 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
330 #elif defined(__APPLE__)
331 strcpy( psz_fontfile, DEFAULT_FONT );
333 msg_Err( p_filter, "user didn't specify a font" );
338 i_error = FT_Init_FreeType( &p_sys->p_library );
341 msg_Err( p_filter, "couldn't initialize freetype" );
344 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
346 if( i_error == FT_Err_Unknown_File_Format )
348 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
353 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
357 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
360 msg_Err( p_filter, "font has no unicode translation table" );
364 #ifdef HAVE_FONTCONFIG
365 p_sys->b_fontconfig_ok = false;
366 p_sys->p_fontconfig = NULL;
367 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
370 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
372 var_Get( p_filter, "freetype-fontsize", &val );
373 p_sys->i_default_font_size = val.i_int;
374 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
376 free( psz_fontfile );
378 p_sys->pp_font_attachments = NULL;
379 p_sys->i_font_attachments = 0;
381 p_filter->pf_render_text = RenderText;
382 #ifdef HAVE_FONTCONFIG
383 p_filter->pf_render_html = RenderHtml;
385 p_filter->pf_render_html = NULL;
388 LoadFontsFromAttachments( p_filter );
393 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
394 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
395 free( psz_fontfile );
400 /*****************************************************************************
401 * Destroy: destroy Clone video thread output method
402 *****************************************************************************
403 * Clean up all data and library connections
404 *****************************************************************************/
405 static void Destroy( vlc_object_t *p_this )
407 filter_t *p_filter = (filter_t *)p_this;
408 filter_sys_t *p_sys = p_filter->p_sys;
410 if( p_sys->pp_font_attachments )
414 for( k = 0; k < p_sys->i_font_attachments; k++ )
415 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
417 free( p_sys->pp_font_attachments );
420 #ifdef HAVE_FONTCONFIG
421 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
424 /* FcFini asserts calling the subfunction FcCacheFini()
425 * even if no other library functions have been made since FcInit(),
426 * so don't call it. */
428 FT_Done_Face( p_sys->p_face );
429 FT_Done_FreeType( p_sys->p_library );
433 #ifdef HAVE_FONTCONFIG
434 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
436 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
438 /* Check for an existing Fontbuilder thread */
439 vlc_mutex_lock( &fb_lock );
440 vlc_object_t *p_fontbuilder =
441 vlc_object_find_name( p_filter->p_libvlc,
442 "fontlist builder", FIND_CHILD );
446 /* Create the FontBuilderThread thread as a child of a top-level
447 * object, so that it can survive the destruction of the
448 * freetype object - the fontlist only needs to be built once,
449 * and calling the fontbuild a second time while the first is
450 * still in progress can cause thread instabilities.
452 * XXX The fontbuilder will be destroy as soon as it is unused.
455 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
456 sizeof(vlc_object_t) );
459 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
460 p_fontbuilder->p_private = NULL;
461 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
463 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
465 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
466 var_SetBool( p_fontbuilder, "build-done", false );
467 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
468 var_SetBool( p_fontbuilder, "build-joined", false );
470 if( vlc_thread_create( p_fontbuilder,
473 VLC_THREAD_PRIORITY_LOW ) )
475 msg_Warn( p_filter, "fontconfig database builder thread can't "
476 "be launched. Font styling support will be limited." );
483 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
484 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
486 vlc_mutex_unlock( &fb_lock );
487 return p_fontbuilder;
489 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
491 vlc_mutex_lock( &fb_lock );
494 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
496 /* We wait for the thread on the first FontBuilderDetach */
497 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
499 var_SetBool( p_fontbuilder, "build-joined", true );
500 vlc_mutex_unlock( &fb_lock );
501 /* We need to unlock otherwise we may not join (the thread waiting
502 * for the lock). It is safe to unlock as no one else will try a
503 * join and we have a reference on the object) */
504 vlc_thread_join( p_fontbuilder );
505 vlc_mutex_lock( &fb_lock );
507 vlc_object_release( p_fontbuilder );
509 vlc_mutex_unlock( &fb_lock );
511 static void* FontBuilderThread( vlc_object_t *p_this )
513 FcConfig *p_fontconfig = FcInitLoadConfig();
518 int canc = vlc_savecancel ();
520 //msg_Dbg( p_this, "Building font database..." );
521 msg_Dbg( p_this, "Building font database..." );
523 if(! FcConfigBuildFonts( p_fontconfig ))
525 /* Don't destroy the fontconfig object - we won't be able to do
526 * italics or bold or change the font face, but we will still
527 * be able to do underline and change the font size.
529 msg_Err( p_this, "fontconfig database can't be built. "
530 "Font styling won't be available" );
534 msg_Dbg( p_this, "Finished building font database." );
535 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
537 vlc_mutex_lock( &fb_lock );
538 p_this->p_private = p_fontconfig;
539 vlc_mutex_unlock( &fb_lock );
541 var_SetBool( p_this, "build-done", true );
542 vlc_restorecancel (canc);
546 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
548 filter_sys_t *p_sys = p_filter->p_sys;
550 p_sys->p_fontconfig = p_fontbuilder->p_private;
551 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
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;
567 vlc_mutex_lock( &fb_lock );
569 FontBuilderGetFcConfig( p_filter, p_this );
571 vlc_mutex_unlock( &fb_lock );
580 /*****************************************************************************
581 * Make any TTF/OTF fonts present in the attachments of the media file
582 * and store them for later use by the FreeType Engine
583 *****************************************************************************/
584 static int LoadFontsFromAttachments( filter_t *p_filter )
586 filter_sys_t *p_sys = p_filter->p_sys;
587 input_thread_t *p_input;
588 input_attachment_t **pp_attachments;
589 int i_attachments_cnt;
591 int rv = VLC_SUCCESS;
593 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
597 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
599 vlc_object_release(p_input);
603 p_sys->i_font_attachments = 0;
604 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
605 if(! p_sys->pp_font_attachments )
608 for( k = 0; k < i_attachments_cnt; k++ )
610 input_attachment_t *p_attach = pp_attachments[k];
612 if( p_sys->pp_font_attachments )
614 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
615 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
616 ( p_attach->i_data > 0 ) &&
617 ( p_attach->p_data != NULL ) )
619 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
623 vlc_input_attachment_Delete( p_attach );
628 vlc_input_attachment_Delete( p_attach );
631 free( pp_attachments );
633 vlc_object_release(p_input);
638 /*****************************************************************************
639 * Render: place string in picture
640 *****************************************************************************
641 * This function merges the previously rendered freetype glyphs into a picture
642 *****************************************************************************/
643 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
644 line_desc_t *p_line, int i_width, int i_height )
646 static const uint8_t pi_gamma[16] =
647 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
648 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
652 int i, x, y, i_pitch;
653 uint8_t i_y; /* YUV values, derived from incoming RGB */
656 /* Create a new subpicture region */
657 memset( &fmt, 0, sizeof(video_format_t) );
658 fmt.i_chroma = VLC_CODEC_YUVP;
660 fmt.i_width = fmt.i_visible_width = i_width + 4;
661 fmt.i_height = fmt.i_visible_height = i_height + 4;
662 if( p_region->fmt.i_visible_width > 0 )
663 fmt.i_visible_width = p_region->fmt.i_visible_width;
664 if( p_region->fmt.i_visible_height > 0 )
665 fmt.i_visible_height = p_region->fmt.i_visible_height;
666 fmt.i_x_offset = fmt.i_y_offset = 0;
668 assert( !p_region->p_picture );
669 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
670 if( !p_region->p_picture )
674 /* Calculate text color components */
675 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
676 25 * p_line->i_blue + 128) >> 8) + 16;
677 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
678 112 * p_line->i_blue + 128) >> 8) + 128;
679 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
680 18 * p_line->i_blue + 128) >> 8) + 128;
683 fmt.p_palette->i_entries = 16;
684 for( i = 0; i < 8; i++ )
686 fmt.p_palette->palette[i][0] = 0;
687 fmt.p_palette->palette[i][1] = 0x80;
688 fmt.p_palette->palette[i][2] = 0x80;
689 fmt.p_palette->palette[i][3] = pi_gamma[i];
690 fmt.p_palette->palette[i][3] =
691 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
693 for( i = 8; i < fmt.p_palette->i_entries; i++ )
695 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
696 fmt.p_palette->palette[i][1] = i_u;
697 fmt.p_palette->palette[i][2] = i_v;
698 fmt.p_palette->palette[i][3] = pi_gamma[i];
699 fmt.p_palette->palette[i][3] =
700 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
703 p_dst = p_region->p_picture->Y_PIXELS;
704 i_pitch = p_region->p_picture->Y_PITCH;
706 /* Initialize the region pixels */
707 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
709 for( ; p_line != NULL; p_line = p_line->p_next )
711 int i_glyph_tmax = 0;
712 int i_bitmap_offset, i_offset, i_align_offset = 0;
713 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
715 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
716 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
719 if( p_line->i_width < i_width )
721 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
723 i_align_offset = i_width - p_line->i_width;
725 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
727 i_align_offset = ( i_width - p_line->i_width ) / 2;
731 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
733 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
735 i_offset = ( p_line->p_glyph_pos[ i ].y +
736 i_glyph_tmax - p_glyph->top + 2 ) *
737 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
740 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
742 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
744 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
746 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
753 /* Outlining (find something better than nearest neighbour filtering ?) */
756 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
757 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
758 uint8_t left, current;
760 for( y = 1; y < (int)fmt.i_height - 1; y++ )
762 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
763 p_dst += p_region->p_picture->Y_PITCH;
766 for( x = 1; x < (int)fmt.i_width - 1; x++ )
769 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
770 p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
774 memset( p_top, 0, fmt.i_width );
780 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
781 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
782 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
783 int i_glyph_tmax, int i_align_offset,
784 uint8_t i_y, uint8_t i_u, uint8_t i_v,
785 subpicture_region_t *p_region)
789 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
791 p_dst_y = p_region->p_picture->Y_PIXELS;
792 p_dst_u = p_region->p_picture->U_PIXELS;
793 p_dst_v = p_region->p_picture->V_PIXELS;
794 p_dst_a = p_region->p_picture->A_PIXELS;
795 i_pitch = p_region->p_picture->A_PITCH;
797 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
798 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
800 for( y = 0; y < i_line_thickness; y++ )
802 int i_extra = p_this_glyph->bitmap.width;
806 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
807 (p_this_glyph_pos->x + p_this_glyph->left);
809 for( x = 0; x < i_extra; x++ )
813 /* break the underline around the tails of any glyphs which cross it */
814 for( z = x - i_line_thickness;
815 z < x + i_line_thickness && b_ok;
818 if( p_next_glyph && ( z >= i_extra ) )
820 int i_row = i_line_offset + p_next_glyph->top + y;
822 if( ( p_next_glyph->bitmap.rows > i_row ) &&
823 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
828 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
830 int i_row = i_line_offset + p_this_glyph->top + y;
832 if( ( p_this_glyph->bitmap.rows > i_row ) &&
833 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
842 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
843 p_dst_u[i_offset+x] = i_u;
844 p_dst_v[i_offset+x] = i_v;
845 p_dst_a[i_offset+x] = 255;
852 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
854 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
855 int i_pitch = p_region->p_picture->A_PITCH;
858 for( ; p_line != NULL; p_line = p_line->p_next )
860 int i_glyph_tmax=0, i = 0;
861 int i_bitmap_offset, i_offset, i_align_offset = 0;
862 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
864 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
865 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
868 if( p_line->i_width < i_width )
870 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
872 i_align_offset = i_width - p_line->i_width;
874 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
876 i_align_offset = ( i_width - p_line->i_width ) / 2;
880 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
882 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
884 i_offset = ( p_line->p_glyph_pos[ i ].y +
885 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
886 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
887 i_align_offset +xoffset;
889 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
891 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
893 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
894 if( p_dst[i_offset+x] <
895 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
897 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
906 /*****************************************************************************
907 * Render: place string in picture
908 *****************************************************************************
909 * This function merges the previously rendered freetype glyphs into a picture
910 *****************************************************************************/
911 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
912 line_desc_t *p_line, int i_width, int i_height )
914 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
916 int i, x, y, i_pitch, i_alpha;
917 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
919 if( i_width == 0 || i_height == 0 )
922 /* Create a new subpicture region */
923 memset( &fmt, 0, sizeof(video_format_t) );
924 fmt.i_chroma = VLC_CODEC_YUVA;
926 fmt.i_width = fmt.i_visible_width = i_width + 6;
927 fmt.i_height = fmt.i_visible_height = i_height + 6;
928 if( p_region->fmt.i_visible_width > 0 )
929 fmt.i_visible_width = p_region->fmt.i_visible_width;
930 if( p_region->fmt.i_visible_height > 0 )
931 fmt.i_visible_height = p_region->fmt.i_visible_height;
932 fmt.i_x_offset = fmt.i_y_offset = 0;
934 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
935 if( !p_region->p_picture )
939 /* Calculate text color components */
940 YUVFromRGB( (p_line->i_red << 16) |
941 (p_line->i_green << 8) |
944 i_alpha = p_line->i_alpha;
946 p_dst_y = p_region->p_picture->Y_PIXELS;
947 p_dst_u = p_region->p_picture->U_PIXELS;
948 p_dst_v = p_region->p_picture->V_PIXELS;
949 p_dst_a = p_region->p_picture->A_PIXELS;
950 i_pitch = p_region->p_picture->A_PITCH;
952 /* Initialize the region pixels */
953 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
955 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
956 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
957 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
958 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
962 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
963 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
964 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
965 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
967 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
968 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
970 DrawBlack( p_line, i_width, p_region, 0, 0);
971 DrawBlack( p_line, i_width, p_region, -1, 0);
972 DrawBlack( p_line, i_width, p_region, 0, -1);
973 DrawBlack( p_line, i_width, p_region, 1, 0);
974 DrawBlack( p_line, i_width, p_region, 0, 1);
977 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
979 DrawBlack( p_line, i_width, p_region, -1, -1);
980 DrawBlack( p_line, i_width, p_region, -1, 1);
981 DrawBlack( p_line, i_width, p_region, 1, -1);
982 DrawBlack( p_line, i_width, p_region, 1, 1);
984 DrawBlack( p_line, i_width, p_region, -2, 0);
985 DrawBlack( p_line, i_width, p_region, 0, -2);
986 DrawBlack( p_line, i_width, p_region, 2, 0);
987 DrawBlack( p_line, i_width, p_region, 0, 2);
989 DrawBlack( p_line, i_width, p_region, -2, -2);
990 DrawBlack( p_line, i_width, p_region, -2, 2);
991 DrawBlack( p_line, i_width, p_region, 2, -2);
992 DrawBlack( p_line, i_width, p_region, 2, 2);
994 DrawBlack( p_line, i_width, p_region, -3, 0);
995 DrawBlack( p_line, i_width, p_region, 0, -3);
996 DrawBlack( p_line, i_width, p_region, 3, 0);
997 DrawBlack( p_line, i_width, p_region, 0, 3);
1000 for( ; p_line != NULL; p_line = p_line->p_next )
1002 int i_glyph_tmax = 0;
1003 int i_bitmap_offset, i_offset, i_align_offset = 0;
1004 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1006 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1007 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1010 if( p_line->i_width < i_width )
1012 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1014 i_align_offset = i_width - p_line->i_width;
1016 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1018 i_align_offset = ( i_width - p_line->i_width ) / 2;
1022 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1024 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1026 i_offset = ( p_line->p_glyph_pos[ i ].y +
1027 i_glyph_tmax - p_glyph->top + 3 ) *
1028 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1031 if( p_line->b_new_color_mode )
1033 /* Every glyph can (and in fact must) have its own color */
1034 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1037 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1039 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1041 uint8_t i_y_local = i_y;
1042 uint8_t i_u_local = i_u;
1043 uint8_t i_v_local = i_v;
1045 if( p_line->p_fg_bg_ratio != 0x00 )
1047 int i_split = p_glyph->bitmap.width *
1048 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1052 YUVFromRGB( p_line->p_bg_rgb[ i ],
1053 &i_y_local, &i_u_local, &i_v_local );
1057 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1059 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1060 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1062 p_dst_u[i_offset+x] = i_u;
1063 p_dst_v[i_offset+x] = i_v;
1065 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1066 p_dst_a[i_offset+x] = 0xff;
1069 i_offset += i_pitch;
1072 if( p_line->pi_underline_thickness[ i ] )
1074 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1075 p_line->pi_underline_offset[ i ],
1076 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1077 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1078 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1079 i_glyph_tmax, i_align_offset,
1086 /* Apply the alpha setting */
1087 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1088 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1094 * This function renders a text subpicture region into another one.
1095 * It also calculates the size needed for this string, and renders the
1096 * needed glyphs into memory. It is used as pf_add_string callback in
1097 * the vout method by this module
1099 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1100 subpicture_region_t *p_region_in )
1102 filter_sys_t *p_sys = p_filter->p_sys;
1103 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1104 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1105 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1106 int i_string_length;
1108 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1109 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1119 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1120 psz_string = p_region_in->psz_text;
1121 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1123 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1124 i_scale = val.i_int;
1126 if( p_region_in->p_style )
1128 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1129 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1130 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1134 i_font_color = p_sys->i_font_color;
1135 i_font_alpha = 255 - p_sys->i_font_opacity;
1136 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1139 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1140 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1141 SetFontSize( p_filter, i_font_size );
1143 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1144 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1145 i_blue = i_font_color & 0x000000FF;
1147 result.x = result.y = 0;
1148 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1150 psz_unicode = psz_unicode_orig =
1151 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1152 if( psz_unicode == NULL )
1154 #if defined(WORDS_BIGENDIAN)
1155 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1157 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1159 if( iconv_handle == (vlc_iconv_t)-1 )
1161 msg_Warn( p_filter, "unable to do conversion" );
1167 const char *p_in_buffer = psz_string;
1168 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1169 i_in_bytes = strlen( psz_string );
1170 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1171 i_out_bytes_left = i_out_bytes;
1172 p_out_buffer = (char *)psz_unicode;
1173 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1175 &p_out_buffer, &i_out_bytes_left );
1177 vlc_iconv_close( iconv_handle );
1181 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1182 "bytes left %u", (unsigned)i_in_bytes );
1185 *(uint32_t*)p_out_buffer = 0;
1186 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1189 #if defined(HAVE_FRIBIDI)
1191 uint32_t *p_fribidi_string;
1192 int32_t start_pos, pos = 0;
1194 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1195 if( !p_fribidi_string )
1198 /* Do bidi conversion line-by-line */
1199 while( pos < i_string_length )
1201 while( pos < i_string_length )
1203 i_char = psz_unicode[pos];
1204 if (i_char != '\r' && i_char != '\n')
1206 p_fribidi_string[pos] = i_char;
1210 while( pos < i_string_length )
1212 i_char = psz_unicode[pos];
1213 if (i_char == '\r' || i_char == '\n')
1217 if (pos > start_pos)
1219 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1220 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1223 (FriBidiChar*)p_fribidi_string + start_pos,
1228 free( psz_unicode_orig );
1229 psz_unicode = psz_unicode_orig = p_fribidi_string;
1230 p_fribidi_string[ i_string_length ] = 0;
1234 /* Calculate relative glyph positions and a bounding box for the
1236 if( !(p_line = NewLine( strlen( psz_string ))) )
1239 i_pen_x = i_pen_y = 0;
1241 psz_line_start = psz_unicode;
1243 #define face p_sys->p_face
1244 #define glyph face->glyph
1246 while( *psz_unicode )
1248 i_char = *psz_unicode++;
1249 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1254 if( i_char == '\n' )
1256 psz_line_start = psz_unicode;
1257 if( !(p_next = NewLine( strlen( psz_string ))) )
1259 p_line->p_next = p_next;
1260 p_line->i_width = line.xMax;
1261 p_line->i_height = face->size->metrics.height >> 6;
1262 p_line->pp_glyphs[ i ] = NULL;
1263 p_line->i_alpha = i_font_alpha;
1264 p_line->i_red = i_red;
1265 p_line->i_green = i_green;
1266 p_line->i_blue = i_blue;
1269 result.x = __MAX( result.x, line.xMax );
1270 result.y += face->size->metrics.height >> 6;
1273 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1274 i_pen_y += face->size->metrics.height >> 6;
1276 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1281 i_glyph_index = FT_Get_Char_Index( face, i_char );
1282 if( p_sys->i_use_kerning && i_glyph_index
1286 FT_Get_Kerning( face, i_previous, i_glyph_index,
1287 ft_kerning_default, &delta );
1288 i_pen_x += delta.x >> 6;
1291 p_line->p_glyph_pos[ i ].x = i_pen_x;
1292 p_line->p_glyph_pos[ i ].y = i_pen_y;
1293 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1296 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1300 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1303 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1307 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1308 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1311 FT_Done_Glyph( tmp_glyph );
1314 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1317 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1318 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1319 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1321 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1322 p_line->pp_glyphs[ i ] = NULL;
1324 p_line = NewLine( strlen( psz_string ));
1325 if( p_prev ) p_prev->p_next = p_line;
1326 else p_lines = p_line;
1328 uint32_t *psz_unicode_saved = psz_unicode;
1329 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1333 if( psz_unicode == psz_line_start )
1334 { /* try harder to break that line */
1335 psz_unicode = psz_unicode_saved;
1336 while( psz_unicode > psz_line_start &&
1337 *psz_unicode != '_' && *psz_unicode != '/' &&
1338 *psz_unicode != '\\' && *psz_unicode != '.' )
1343 if( psz_unicode == psz_line_start )
1345 msg_Warn( p_filter, "unbreakable string" );
1350 *psz_unicode = '\n';
1352 psz_unicode = psz_line_start;
1355 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1358 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1359 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1361 i_previous = i_glyph_index;
1362 i_pen_x += glyph->advance.x >> 6;
1366 p_line->i_width = line.xMax;
1367 p_line->i_height = face->size->metrics.height >> 6;
1368 p_line->pp_glyphs[ i ] = NULL;
1369 p_line->i_alpha = i_font_alpha;
1370 p_line->i_red = i_red;
1371 p_line->i_green = i_green;
1372 p_line->i_blue = i_blue;
1373 result.x = __MAX( result.x, line.xMax );
1374 result.y += line.yMax - line.yMin;
1379 p_region_out->i_x = p_region_in->i_x;
1380 p_region_out->i_y = p_region_in->i_y;
1382 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1383 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1385 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1387 free( psz_unicode_orig );
1388 FreeLines( p_lines );
1392 free( psz_unicode_orig );
1393 FreeLines( p_lines );
1394 return VLC_EGENERIC;
1397 #ifdef HAVE_FONTCONFIG
1398 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1399 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1400 bool b_italic, bool b_uline )
1402 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1406 p_style->i_font_size = i_font_size;
1407 p_style->i_font_color = i_font_color;
1408 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1409 p_style->b_italic = b_italic;
1410 p_style->b_bold = b_bold;
1411 p_style->b_underline = b_uline;
1413 p_style->psz_fontname = strdup( psz_fontname );
1418 static void DeleteStyle( ft_style_t *p_style )
1422 free( p_style->psz_fontname );
1427 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1434 if(( s1->i_font_size == s2->i_font_size ) &&
1435 ( s1->i_font_color == s2->i_font_color ) &&
1436 ( s1->b_italic == s2->b_italic ) &&
1437 ( s1->b_bold == s2->b_bold ) &&
1438 ( s1->b_underline == s2->b_underline ) &&
1439 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1446 static void IconvText( filter_t *p_filter, const char *psz_string,
1447 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1449 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1451 /* If memory hasn't been allocated for our output string, allocate it here
1452 * - the calling function must now be responsible for freeing it.
1454 if( !*ppsz_unicode )
1455 *ppsz_unicode = (uint32_t *)
1456 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1458 /* We don't need to handle a NULL pointer in *ppsz_unicode
1459 * if we are instead testing for a non NULL value like we are here */
1463 #if defined(WORDS_BIGENDIAN)
1464 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1466 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1468 if( iconv_handle != (vlc_iconv_t)-1 )
1470 char *p_in_buffer, *p_out_buffer;
1471 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1472 i_in_bytes = strlen( psz_string );
1473 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1474 i_out_bytes_left = i_out_bytes;
1475 p_in_buffer = (char *) psz_string;
1476 p_out_buffer = (char *) *ppsz_unicode;
1477 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1478 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1480 vlc_iconv_close( iconv_handle );
1484 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1485 "bytes left %u", (unsigned)i_in_bytes );
1489 *(uint32_t*)p_out_buffer = 0;
1491 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1496 msg_Warn( p_filter, "unable to do conversion" );
1501 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1502 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1505 ft_style_t *p_style = NULL;
1507 char *psz_fontname = NULL;
1508 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1509 uint32_t i_karaoke_bg_color = i_font_color;
1510 int i_font_size = p_sys->i_font_size;
1512 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1513 &i_font_color, &i_karaoke_bg_color ))
1515 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1516 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1521 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1522 bool b_uline, int i_karaoke_bgcolor,
1523 line_desc_t *p_line, uint32_t *psz_unicode,
1524 int *pi_pen_x, int i_pen_y, int *pi_start,
1525 FT_Vector *p_result )
1530 bool b_first_on_line = true;
1533 int i_pen_x_start = *pi_pen_x;
1535 uint32_t *psz_unicode_start = psz_unicode;
1537 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1539 /* Account for part of line already in position */
1540 for( i=0; i<*pi_start; i++ )
1544 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1545 ft_glyph_bbox_pixels, &glyph_size );
1547 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1548 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1549 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1550 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1556 b_first_on_line = false;
1558 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1564 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1565 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1569 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1570 ft_kerning_default, &delta );
1571 *pi_pen_x += delta.x >> 6;
1573 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1574 p_line->p_glyph_pos[ i ].y = i_pen_y;
1576 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1580 "unable to render text FT_Load_Glyph returned %d", i_error );
1581 p_line->pp_glyphs[ i ] = NULL;
1582 return VLC_EGENERIC;
1584 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1588 "unable to render text FT_Get_Glyph returned %d", i_error );
1589 p_line->pp_glyphs[ i ] = NULL;
1590 return VLC_EGENERIC;
1592 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1593 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1596 FT_Done_Glyph( tmp_glyph );
1601 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1602 p_face->size->metrics.y_scale));
1603 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1604 p_face->size->metrics.y_scale));
1606 p_line->pi_underline_offset[ i ] =
1607 ( aOffset < 0 ) ? -aOffset : aOffset;
1608 p_line->pi_underline_thickness[ i ] =
1609 ( aSize < 0 ) ? -aSize : aSize;
1611 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1612 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1613 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1614 p_line->p_fg_bg_ratio[ i ] = 0x00;
1616 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1617 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1618 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1620 for( ; i >= *pi_start; i-- )
1621 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1624 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1628 if( psz_unicode == psz_unicode_start )
1630 if( b_first_on_line )
1632 msg_Warn( p_filter, "unbreakable string" );
1633 p_line->pp_glyphs[ i ] = NULL;
1634 return VLC_EGENERIC;
1636 *pi_pen_x = i_pen_x_start;
1638 p_line->i_width = line.xMax;
1639 p_line->i_height = __MAX( p_line->i_height,
1640 p_face->size->metrics.height >> 6 );
1641 p_line->pp_glyphs[ i ] = NULL;
1643 p_result->x = __MAX( p_result->x, line.xMax );
1644 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1645 i_yMax - i_yMin ) );
1650 *psz_unicode = '\n';
1652 psz_unicode = psz_unicode_start;
1653 *pi_pen_x = i_pen_x_start;
1661 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1662 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1664 i_previous = i_glyph_index;
1665 *pi_pen_x += p_face->glyph->advance.x >> 6;
1668 p_line->i_width = line.xMax;
1669 p_line->i_height = __MAX( p_line->i_height,
1670 p_face->size->metrics.height >> 6 );
1671 p_line->pp_glyphs[ i ] = NULL;
1673 p_result->x = __MAX( p_result->x, line.xMax );
1674 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1675 line.yMax - line.yMin ) );
1679 /* Get rid of any text processed - if necessary repositioning
1680 * at the start of a new line of text
1684 *psz_unicode_start = '\0';
1686 else if( psz_unicode > psz_unicode_start )
1688 for( i=0; psz_unicode[ i ]; i++ )
1689 psz_unicode_start[ i ] = psz_unicode[ i ];
1690 psz_unicode_start[ i ] = '\0';
1696 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1697 uint32_t **psz_text_out, uint32_t *pi_runs,
1698 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1699 ft_style_t *p_style )
1701 uint32_t i_string_length = 0;
1703 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1704 *psz_text_out += i_string_length;
1706 if( ppp_styles && ppi_run_lengths )
1712 *ppp_styles = (ft_style_t **)
1713 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1715 else if( *pi_runs == 1 )
1717 *ppp_styles = (ft_style_t **)
1718 malloc( *pi_runs * sizeof( ft_style_t * ) );
1721 /* We have just malloc'ed this memory successfully -
1722 * *pi_runs HAS to be within the memory area of *ppp_styles */
1725 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1729 if( *ppi_run_lengths )
1731 *ppi_run_lengths = (uint32_t *)
1732 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1734 else if( *pi_runs == 1 )
1736 *ppi_run_lengths = (uint32_t *)
1737 malloc( *pi_runs * sizeof( uint32_t ) );
1740 /* same remarks here */
1741 if( *ppi_run_lengths )
1743 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1746 /* If we couldn't use the p_style argument due to memory allocation
1747 * problems above, release it here.
1749 if( p_style ) DeleteStyle( p_style );
1752 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1756 for( k=0; k < p_sys->i_font_attachments; k++ )
1758 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1760 FT_Face p_face = NULL;
1762 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1770 bool match = !strcasecmp( p_face->family_name,
1771 p_style->psz_fontname );
1773 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1774 match = match && p_style->b_bold;
1776 match = match && !p_style->b_bold;
1778 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1779 match = match && p_style->b_italic;
1781 match = match && !p_style->b_italic;
1789 FT_Done_Face( p_face );
1794 return VLC_EGENERIC;
1797 static int ProcessLines( filter_t *p_filter,
1802 uint32_t *pi_run_lengths,
1803 ft_style_t **pp_styles,
1804 line_desc_t **pp_lines,
1806 FT_Vector *p_result,
1810 uint32_t *pi_k_run_lengths,
1811 uint32_t *pi_k_durations )
1813 filter_sys_t *p_sys = p_filter->p_sys;
1814 ft_style_t **pp_char_styles;
1815 int *p_new_positions = NULL;
1816 int8_t *p_levels = NULL;
1817 uint8_t *pi_karaoke_bar = NULL;
1821 /* Assign each character in the text string its style explicitly, so that
1822 * after the characters have been shuffled around by Fribidi, we can re-apply
1823 * the styles, and to simplify the calculation of runs within a line.
1825 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1826 if( !pp_char_styles )
1831 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1832 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1833 * we just won't be able to display the progress bar; at least we'll
1839 for( j = 0; j < i_runs; j++ )
1840 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1841 pp_char_styles[ i++ ] = pp_styles[ j ];
1843 #if defined(HAVE_FRIBIDI)
1845 ft_style_t **pp_char_styles_new;
1846 int *p_old_positions;
1847 uint32_t *p_fribidi_string;
1848 int start_pos, pos = 0;
1850 pp_char_styles_new = (ft_style_t **)
1851 malloc( i_len * sizeof( ft_style_t * ));
1853 p_fribidi_string = (uint32_t *)
1854 malloc( (i_len + 1) * sizeof(uint32_t) );
1855 p_old_positions = (int *)
1856 malloc( (i_len + 1) * sizeof( int ) );
1857 p_new_positions = (int *)
1858 malloc( (i_len + 1) * sizeof( int ) );
1859 p_levels = (int8_t *)
1860 malloc( (i_len + 1) * sizeof( int8_t ) );
1862 if( ! pp_char_styles_new ||
1863 ! p_fribidi_string ||
1864 ! p_old_positions ||
1865 ! p_new_positions ||
1869 free( p_old_positions );
1870 free( p_new_positions );
1871 free( p_fribidi_string );
1872 free( pp_char_styles_new );
1873 free( pi_karaoke_bar );
1875 free( pp_char_styles );
1879 /* Do bidi conversion line-by-line */
1882 while(pos < i_len) {
1883 if (psz_text[pos] != '\n')
1885 p_fribidi_string[pos] = psz_text[pos];
1886 pp_char_styles_new[pos] = pp_char_styles[pos];
1887 p_new_positions[pos] = pos;
1892 while(pos < i_len) {
1893 if (psz_text[pos] == '\n')
1897 if (pos > start_pos)
1899 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1900 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1901 pos - start_pos, &base_dir,
1902 (FriBidiChar*)p_fribidi_string + start_pos,
1903 p_new_positions + start_pos,
1905 p_levels + start_pos );
1906 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1908 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1909 p_old_positions[ j - start_pos ] ];
1910 p_new_positions[ j ] += start_pos;
1914 free( p_old_positions );
1915 free( pp_char_styles );
1916 pp_char_styles = pp_char_styles_new;
1917 psz_text = p_fribidi_string;
1918 p_fribidi_string[ i_len ] = 0;
1921 /* Work out the karaoke */
1922 if( pi_karaoke_bar )
1924 int64_t i_last_duration = 0;
1925 int64_t i_duration = 0;
1926 int64_t i_start_pos = 0;
1927 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1929 for( k = 0; k< i_k_runs; k++ )
1931 double fraction = 0.0;
1933 i_duration += pi_k_durations[ k ];
1935 if( i_duration < i_elapsed )
1937 /* Completely finished this run-length -
1938 * let it render normally */
1942 else if( i_elapsed < i_last_duration )
1944 /* Haven't got up to this segment yet -
1945 * render it completely in karaoke BG mode */
1951 /* Partway through this run */
1953 fraction = (double)(i_elapsed - i_last_duration) /
1954 (double)pi_k_durations[ k ];
1956 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1958 double shade = pi_k_run_lengths[ k ] * fraction;
1960 if( p_new_positions )
1961 j = p_new_positions[ i_start_pos + i ];
1963 j = i_start_pos + i;
1965 if( i < (uint32_t)shade )
1966 pi_karaoke_bar[ j ] = 0xff;
1967 else if( (double)i > shade )
1968 pi_karaoke_bar[ j ] = 0x00;
1971 shade -= (int)shade;
1972 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1973 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1977 i_last_duration = i_duration;
1978 i_start_pos += pi_k_run_lengths[ k ];
1982 free( p_new_positions );
1984 FT_Vector tmp_result;
1986 line_desc_t *p_line = NULL;
1987 line_desc_t *p_prev = NULL;
1993 p_result->x = p_result->y = 0;
1994 tmp_result.x = tmp_result.y = 0;
1997 for( k = 0; k <= (uint32_t) i_len; k++ )
1999 if( ( k == (uint32_t) i_len ) ||
2001 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2003 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2005 /* End of the current style run */
2006 FT_Face p_face = NULL;
2009 /* Look for a match amongst our attachments first */
2010 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2014 char *psz_fontfile = NULL;
2016 vlc_mutex_lock( &fb_lock );
2017 if( p_sys->b_fontconfig_ok )
2019 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2020 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2021 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2022 p_style->psz_fontname,
2027 vlc_mutex_unlock( &fb_lock );
2029 if( psz_fontfile && ! *psz_fontfile )
2031 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2032 " so using default font", p_style->psz_fontname,
2033 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2034 (p_style->b_bold ? "(Bold)" :
2035 (p_style->b_italic ? "(Italic)" : ""))) );
2036 free( psz_fontfile );
2037 psz_fontfile = NULL;
2042 if( FT_New_Face( p_sys->p_library,
2043 psz_fontfile, i_idx, &p_face ) )
2045 free( psz_fontfile );
2046 free( pp_char_styles );
2047 #if defined(HAVE_FRIBIDI)
2050 free( pi_karaoke_bar );
2051 return VLC_EGENERIC;
2053 free( psz_fontfile );
2057 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2059 /* We've loaded a font face which is unhelpful for actually
2060 * rendering text - fallback to the default one.
2062 FT_Done_Face( p_face );
2066 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2067 ft_encoding_unicode ) ||
2068 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2069 p_style->i_font_size ) )
2071 if( p_face ) FT_Done_Face( p_face );
2072 free( pp_char_styles );
2073 #if defined(HAVE_FRIBIDI)
2076 free( pi_karaoke_bar );
2077 return VLC_EGENERIC;
2079 p_sys->i_use_kerning =
2080 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2083 uint32_t *psz_unicode = (uint32_t *)
2084 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2087 if( p_face ) FT_Done_Face( p_face );
2088 free( pp_char_styles );
2089 free( psz_unicode );
2090 #if defined(HAVE_FRIBIDI)
2093 free( pi_karaoke_bar );
2096 memcpy( psz_unicode, psz_text + i_prev,
2097 sizeof( uint32_t ) * ( k - i_prev ) );
2098 psz_unicode[ k - i_prev ] = 0;
2099 while( *psz_unicode )
2103 if( !(p_line = NewLine( i_len - i_prev)) )
2105 if( p_face ) FT_Done_Face( p_face );
2106 free( pp_char_styles );
2107 free( psz_unicode );
2108 #if defined(HAVE_FRIBIDI)
2111 free( pi_karaoke_bar );
2114 /* New Color mode only works in YUVA rendering mode --
2115 * (RGB mode has palette constraints on it). We therefore
2116 * need to populate the legacy colour fields also.
2118 p_line->b_new_color_mode = true;
2119 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2120 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2121 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2122 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2123 p_line->p_next = NULL;
2125 i_pen_y += tmp_result.y;
2129 if( p_prev ) p_prev->p_next = p_line;
2130 else *pp_lines = p_line;
2133 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2134 p_style->i_font_color, p_style->b_underline,
2135 p_style->i_karaoke_bg_color,
2136 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2137 &tmp_result ) != VLC_SUCCESS )
2139 if( p_face ) FT_Done_Face( p_face );
2140 free( pp_char_styles );
2141 free( psz_unicode );
2142 #if defined(HAVE_FRIBIDI)
2145 free( pi_karaoke_bar );
2146 return VLC_EGENERIC;
2151 p_result->x = __MAX( p_result->x, tmp_result.x );
2152 p_result->y += tmp_result.y;
2157 if( *psz_unicode == '\n')
2161 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2163 *c_ptr = *(c_ptr+1);
2168 free( psz_unicode );
2169 if( p_face ) FT_Done_Face( p_face );
2173 free( pp_char_styles );
2174 #if defined(HAVE_FRIBIDI)
2179 p_result->x = __MAX( p_result->x, tmp_result.x );
2180 p_result->y += tmp_result.y;
2183 if( pi_karaoke_bar )
2186 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2188 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2190 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2194 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2196 /* 100% BG colour will render faster if we
2197 * instead make it 100% FG colour, so leave
2198 * the ratio alone and copy the value across
2200 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2204 if( pi_karaoke_bar[ i ] & 0x80 )
2206 /* Swap Left and Right sides over for Right aligned
2207 * language text (eg. Arabic, Hebrew)
2209 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2211 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2212 p_line->p_bg_rgb[ k ] = i_tmp;
2214 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2217 /* Jump over the '\n' at the line-end */
2220 free( pi_karaoke_bar );
2226 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2227 subpicture_region_t *p_region_in )
2229 int rv = VLC_SUCCESS;
2230 stream_t *p_sub = NULL;
2231 xml_t *p_xml = NULL;
2232 xml_reader_t *p_xml_reader = NULL;
2234 if( !p_region_in || !p_region_in->psz_html )
2235 return VLC_EGENERIC;
2237 /* Reset the default fontsize in case screen metrics have changed */
2238 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2240 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2241 (uint8_t *) p_region_in->psz_html,
2242 strlen( p_region_in->psz_html ),
2246 p_xml = xml_Create( p_filter );
2249 bool b_karaoke = false;
2251 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2254 /* Look for Root Node */
2255 if( xml_ReaderRead( p_xml_reader ) == 1 )
2257 char *psz_node = xml_ReaderName( p_xml_reader );
2259 if( !strcasecmp( "karaoke", psz_node ) )
2261 /* We're going to have to render the text a number
2262 * of times to show the progress marker on the text.
2264 var_SetBool( p_filter, "text-rerender", true );
2267 else if( !strcasecmp( "text", psz_node ) )
2273 /* Only text and karaoke tags are supported */
2274 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2275 xml_ReaderDelete( p_xml, p_xml_reader );
2276 p_xml_reader = NULL;
2288 uint32_t i_runs = 0;
2289 uint32_t i_k_runs = 0;
2290 uint32_t *pi_run_lengths = NULL;
2291 uint32_t *pi_k_run_lengths = NULL;
2292 uint32_t *pi_k_durations = NULL;
2293 ft_style_t **pp_styles = NULL;
2295 line_desc_t *p_lines = NULL;
2297 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2298 sizeof( uint32_t ) );
2303 rv = ProcessNodes( p_filter, p_xml_reader,
2304 p_region_in->p_style, psz_text, &i_len,
2305 &i_runs, &pi_run_lengths, &pp_styles,
2307 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2310 p_region_out->i_x = p_region_in->i_x;
2311 p_region_out->i_y = p_region_in->i_y;
2313 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2315 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2316 pi_run_lengths, pp_styles, &p_lines, &result,
2317 b_karaoke, i_k_runs, pi_k_run_lengths,
2321 for( k=0; k<i_runs; k++)
2322 DeleteStyle( pp_styles[k] );
2324 free( pi_run_lengths );
2327 /* Don't attempt to render text that couldn't be layed out
2330 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2332 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2334 Render( p_filter, p_region_out, p_lines,
2335 result.x, result.y );
2339 RenderYUVA( p_filter, p_region_out, p_lines,
2340 result.x, result.y );
2344 FreeLines( p_lines );
2346 xml_ReaderDelete( p_xml, p_xml_reader );
2348 xml_Delete( p_xml );
2350 stream_Delete( p_sub );
2356 static char* FontConfig_Select( FcConfig* priv, const char* family,
2357 bool b_bold, bool b_italic, int *i_idx )
2360 FcPattern *pat, *p_pat;
2364 pat = FcPatternCreate();
2365 if (!pat) return NULL;
2367 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2368 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2369 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2370 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2372 FcDefaultSubstitute( pat );
2374 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2376 FcPatternDestroy( pat );
2380 p_pat = FcFontMatch( priv, pat, &result );
2381 FcPatternDestroy( pat );
2382 if( !p_pat ) return NULL;
2384 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2385 || ( val_b != FcTrue ) )
2387 FcPatternDestroy( p_pat );
2390 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2395 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2397 FcPatternDestroy( p_pat );
2402 if( strcasecmp((const char*)val_s, family ) != 0 )
2403 msg_Warn( p_filter, "fontconfig: selected font family is not"
2404 "the requested one: '%s' != '%s'\n",
2405 (const char*)val_s, family );
2408 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2410 FcPatternDestroy( p_pat );
2414 FcPatternDestroy( p_pat );
2415 return strdup( (const char*)val_s );
2419 static void FreeLine( line_desc_t *p_line )
2422 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2424 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2426 free( p_line->pp_glyphs );
2427 free( p_line->p_glyph_pos );
2428 free( p_line->p_fg_rgb );
2429 free( p_line->p_bg_rgb );
2430 free( p_line->p_fg_bg_ratio );
2431 free( p_line->pi_underline_offset );
2432 free( p_line->pi_underline_thickness );
2436 static void FreeLines( line_desc_t *p_lines )
2438 line_desc_t *p_line, *p_next;
2440 if( !p_lines ) return;
2442 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2444 p_next = p_line->p_next;
2449 static line_desc_t *NewLine( int i_count )
2451 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2453 if( !p_line ) return NULL;
2454 p_line->i_height = 0;
2455 p_line->i_width = 0;
2456 p_line->p_next = NULL;
2458 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2459 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2460 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2461 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2462 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2463 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2464 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2465 if( ( p_line->pp_glyphs == NULL ) ||
2466 ( p_line->p_glyph_pos == NULL ) ||
2467 ( p_line->p_fg_rgb == NULL ) ||
2468 ( p_line->p_bg_rgb == NULL ) ||
2469 ( p_line->p_fg_bg_ratio == NULL ) ||
2470 ( p_line->pi_underline_offset == NULL ) ||
2471 ( p_line->pi_underline_thickness == NULL ) )
2473 free( p_line->pi_underline_thickness );
2474 free( p_line->pi_underline_offset );
2475 free( p_line->p_fg_rgb );
2476 free( p_line->p_bg_rgb );
2477 free( p_line->p_fg_bg_ratio );
2478 free( p_line->p_glyph_pos );
2479 free( p_line->pp_glyphs );
2483 p_line->pp_glyphs[0] = NULL;
2484 p_line->b_new_color_mode = false;
2489 static int GetFontSize( filter_t *p_filter )
2491 filter_sys_t *p_sys = p_filter->p_sys;
2495 if( p_sys->i_default_font_size )
2497 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2498 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2500 i_size = p_sys->i_default_font_size;
2504 var_Get( p_filter, "freetype-rel-fontsize", &val );
2507 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2508 p_filter->p_sys->i_display_height =
2509 p_filter->fmt_out.video.i_height;
2514 msg_Warn( p_filter, "invalid fontsize, using 12" );
2515 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2516 i_size = 12 * val.i_int / 1000;
2523 static int SetFontSize( filter_t *p_filter, int i_size )
2525 filter_sys_t *p_sys = p_filter->p_sys;
2529 i_size = GetFontSize( p_filter );
2531 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2534 p_sys->i_font_size = i_size;
2536 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2538 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2539 return VLC_EGENERIC;
2545 static void YUVFromRGB( uint32_t i_argb,
2546 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2548 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2549 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2550 int i_blue = ( i_argb & 0x000000ff );
2552 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2553 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2554 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2555 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2556 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2557 -585 * i_blue + 4096 + 1048576) >> 13, 240);