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." );
481 vlc_object_hold( p_fontbuilder );
484 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
485 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
487 vlc_mutex_unlock( &fb_lock );
488 return p_fontbuilder;
490 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
492 vlc_mutex_lock( &fb_lock );
495 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
497 /* We wait for the thread on the first FontBuilderDetach */
498 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
500 var_SetBool( p_fontbuilder, "build-joined", true );
501 vlc_mutex_unlock( &fb_lock );
502 /* We need to unlock otherwise we may not join (the thread waiting
503 * for the lock). It is safe to unlock as no one else will try a
504 * join and we have a reference on the object) */
505 vlc_thread_join( p_fontbuilder );
506 vlc_mutex_lock( &fb_lock );
508 vlc_object_release( p_fontbuilder );
510 vlc_mutex_unlock( &fb_lock );
512 static void* FontBuilderThread( vlc_object_t *p_this )
514 FcConfig *p_fontconfig = FcInitLoadConfig();
519 int canc = vlc_savecancel ();
521 //msg_Dbg( p_this, "Building font database..." );
522 msg_Dbg( p_this, "Building font database..." );
524 if(! FcConfigBuildFonts( p_fontconfig ))
526 /* Don't destroy the fontconfig object - we won't be able to do
527 * italics or bold or change the font face, but we will still
528 * be able to do underline and change the font size.
530 msg_Err( p_this, "fontconfig database can't be built. "
531 "Font styling won't be available" );
535 msg_Dbg( p_this, "Finished building font database." );
536 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
538 vlc_mutex_lock( &fb_lock );
539 p_this->p_private = p_fontconfig;
540 vlc_mutex_unlock( &fb_lock );
542 var_SetBool( p_this, "build-done", true );
543 vlc_restorecancel (canc);
547 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
549 filter_sys_t *p_sys = p_filter->p_sys;
551 p_sys->p_fontconfig = p_fontbuilder->p_private;
552 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
554 static void FontBuilderDestructor( vlc_object_t *p_this )
556 FcConfig *p_fontconfig = p_this->p_private;
559 FcConfigDestroy( p_fontconfig );
561 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
562 vlc_value_t oldval, vlc_value_t newval, void *param )
564 filter_t *p_filter = param;
568 vlc_mutex_lock( &fb_lock );
570 FontBuilderGetFcConfig( p_filter, p_this );
572 vlc_mutex_unlock( &fb_lock );
581 /*****************************************************************************
582 * Make any TTF/OTF fonts present in the attachments of the media file
583 * and store them for later use by the FreeType Engine
584 *****************************************************************************/
585 static int LoadFontsFromAttachments( filter_t *p_filter )
587 filter_sys_t *p_sys = p_filter->p_sys;
588 input_thread_t *p_input;
589 input_attachment_t **pp_attachments;
590 int i_attachments_cnt;
592 int rv = VLC_SUCCESS;
594 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
598 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
600 vlc_object_release(p_input);
604 p_sys->i_font_attachments = 0;
605 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
606 if(! p_sys->pp_font_attachments )
609 for( k = 0; k < i_attachments_cnt; k++ )
611 input_attachment_t *p_attach = pp_attachments[k];
613 if( p_sys->pp_font_attachments )
615 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
616 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
617 ( p_attach->i_data > 0 ) &&
618 ( p_attach->p_data != NULL ) )
620 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
624 vlc_input_attachment_Delete( p_attach );
629 vlc_input_attachment_Delete( p_attach );
632 free( pp_attachments );
634 vlc_object_release(p_input);
639 /*****************************************************************************
640 * Render: place string in picture
641 *****************************************************************************
642 * This function merges the previously rendered freetype glyphs into a picture
643 *****************************************************************************/
644 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
645 line_desc_t *p_line, int i_width, int i_height )
647 static const uint8_t pi_gamma[16] =
648 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
649 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
653 int i, x, y, i_pitch;
654 uint8_t i_y; /* YUV values, derived from incoming RGB */
657 /* Create a new subpicture region */
658 memset( &fmt, 0, sizeof(video_format_t) );
659 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
661 fmt.i_width = fmt.i_visible_width = i_width + 4;
662 fmt.i_height = fmt.i_visible_height = i_height + 4;
663 if( p_region->fmt.i_visible_width > 0 )
664 fmt.i_visible_width = p_region->fmt.i_visible_width;
665 if( p_region->fmt.i_visible_height > 0 )
666 fmt.i_visible_height = p_region->fmt.i_visible_height;
667 fmt.i_x_offset = fmt.i_y_offset = 0;
669 assert( !p_region->p_picture );
670 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
671 if( !p_region->p_picture )
675 /* Calculate text color components */
676 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
677 25 * p_line->i_blue + 128) >> 8) + 16;
678 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
679 112 * p_line->i_blue + 128) >> 8) + 128;
680 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
681 18 * p_line->i_blue + 128) >> 8) + 128;
684 fmt.p_palette->i_entries = 16;
685 for( i = 0; i < 8; i++ )
687 fmt.p_palette->palette[i][0] = 0;
688 fmt.p_palette->palette[i][1] = 0x80;
689 fmt.p_palette->palette[i][2] = 0x80;
690 fmt.p_palette->palette[i][3] = pi_gamma[i];
691 fmt.p_palette->palette[i][3] =
692 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
694 for( i = 8; i < fmt.p_palette->i_entries; i++ )
696 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
697 fmt.p_palette->palette[i][1] = i_u;
698 fmt.p_palette->palette[i][2] = i_v;
699 fmt.p_palette->palette[i][3] = pi_gamma[i];
700 fmt.p_palette->palette[i][3] =
701 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
704 p_dst = p_region->p_picture->Y_PIXELS;
705 i_pitch = p_region->p_picture->Y_PITCH;
707 /* Initialize the region pixels */
708 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
710 for( ; p_line != NULL; p_line = p_line->p_next )
712 int i_glyph_tmax = 0;
713 int i_bitmap_offset, i_offset, i_align_offset = 0;
714 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
716 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
717 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
720 if( p_line->i_width < i_width )
722 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
724 i_align_offset = i_width - p_line->i_width;
726 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
728 i_align_offset = ( i_width - p_line->i_width ) / 2;
732 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
734 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
736 i_offset = ( p_line->p_glyph_pos[ i ].y +
737 i_glyph_tmax - p_glyph->top + 2 ) *
738 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
741 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
743 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
745 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
747 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
754 /* Outlining (find something better than nearest neighbour filtering ?) */
757 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
758 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
759 uint8_t left, current;
761 for( y = 1; y < (int)fmt.i_height - 1; y++ )
763 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
764 p_dst += p_region->p_picture->Y_PITCH;
767 for( x = 1; x < (int)fmt.i_width - 1; x++ )
770 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
771 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;
775 memset( p_top, 0, fmt.i_width );
781 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
782 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
783 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
784 int i_glyph_tmax, int i_align_offset,
785 uint8_t i_y, uint8_t i_u, uint8_t i_v,
786 subpicture_region_t *p_region)
790 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
792 p_dst_y = p_region->p_picture->Y_PIXELS;
793 p_dst_u = p_region->p_picture->U_PIXELS;
794 p_dst_v = p_region->p_picture->V_PIXELS;
795 p_dst_a = p_region->p_picture->A_PIXELS;
796 i_pitch = p_region->p_picture->A_PITCH;
798 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
799 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
801 for( y = 0; y < i_line_thickness; y++ )
803 int i_extra = p_this_glyph->bitmap.width;
807 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
808 (p_this_glyph_pos->x + p_this_glyph->left);
810 for( x = 0; x < i_extra; x++ )
814 /* break the underline around the tails of any glyphs which cross it */
815 for( z = x - i_line_thickness;
816 z < x + i_line_thickness && b_ok;
819 if( p_next_glyph && ( z >= i_extra ) )
821 int i_row = i_line_offset + p_next_glyph->top + y;
823 if( ( p_next_glyph->bitmap.rows > i_row ) &&
824 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
829 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
831 int i_row = i_line_offset + p_this_glyph->top + y;
833 if( ( p_this_glyph->bitmap.rows > i_row ) &&
834 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
843 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
844 p_dst_u[i_offset+x] = i_u;
845 p_dst_v[i_offset+x] = i_v;
846 p_dst_a[i_offset+x] = 255;
853 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
855 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
856 int i_pitch = p_region->p_picture->A_PITCH;
859 for( ; p_line != NULL; p_line = p_line->p_next )
861 int i_glyph_tmax=0, i = 0;
862 int i_bitmap_offset, i_offset, i_align_offset = 0;
863 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
865 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
866 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
869 if( p_line->i_width < i_width )
871 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
873 i_align_offset = i_width - p_line->i_width;
875 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
877 i_align_offset = ( i_width - p_line->i_width ) / 2;
881 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
883 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
885 i_offset = ( p_line->p_glyph_pos[ i ].y +
886 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
887 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
888 i_align_offset +xoffset;
890 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
892 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
894 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
895 if( p_dst[i_offset+x] <
896 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
898 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
907 /*****************************************************************************
908 * Render: place string in picture
909 *****************************************************************************
910 * This function merges the previously rendered freetype glyphs into a picture
911 *****************************************************************************/
912 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
913 line_desc_t *p_line, int i_width, int i_height )
915 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
917 int i, x, y, i_pitch, i_alpha;
918 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
920 if( i_width == 0 || i_height == 0 )
923 /* Create a new subpicture region */
924 memset( &fmt, 0, sizeof(video_format_t) );
925 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
927 fmt.i_width = fmt.i_visible_width = i_width + 6;
928 fmt.i_height = fmt.i_visible_height = i_height + 6;
929 if( p_region->fmt.i_visible_width > 0 )
930 fmt.i_visible_width = p_region->fmt.i_visible_width;
931 if( p_region->fmt.i_visible_height > 0 )
932 fmt.i_visible_height = p_region->fmt.i_visible_height;
933 fmt.i_x_offset = fmt.i_y_offset = 0;
935 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
936 if( !p_region->p_picture )
940 /* Calculate text color components */
941 YUVFromRGB( (p_line->i_red << 16) |
942 (p_line->i_green << 8) |
945 i_alpha = p_line->i_alpha;
947 p_dst_y = p_region->p_picture->Y_PIXELS;
948 p_dst_u = p_region->p_picture->U_PIXELS;
949 p_dst_v = p_region->p_picture->V_PIXELS;
950 p_dst_a = p_region->p_picture->A_PIXELS;
951 i_pitch = p_region->p_picture->A_PITCH;
953 /* Initialize the region pixels */
954 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
956 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
957 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
958 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
959 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
963 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
964 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
965 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
966 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
968 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
969 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
971 DrawBlack( p_line, i_width, p_region, 0, 0);
972 DrawBlack( p_line, i_width, p_region, -1, 0);
973 DrawBlack( p_line, i_width, p_region, 0, -1);
974 DrawBlack( p_line, i_width, p_region, 1, 0);
975 DrawBlack( p_line, i_width, p_region, 0, 1);
978 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
983 DrawBlack( p_line, i_width, p_region, 1, 1);
985 DrawBlack( p_line, i_width, p_region, -2, 0);
986 DrawBlack( p_line, i_width, p_region, 0, -2);
987 DrawBlack( p_line, i_width, p_region, 2, 0);
988 DrawBlack( p_line, i_width, p_region, 0, 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);
993 DrawBlack( p_line, i_width, p_region, 2, 2);
995 DrawBlack( p_line, i_width, p_region, -3, 0);
996 DrawBlack( p_line, i_width, p_region, 0, -3);
997 DrawBlack( p_line, i_width, p_region, 3, 0);
998 DrawBlack( p_line, i_width, p_region, 0, 3);
1001 for( ; p_line != NULL; p_line = p_line->p_next )
1003 int i_glyph_tmax = 0;
1004 int i_bitmap_offset, i_offset, i_align_offset = 0;
1005 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1007 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1008 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1011 if( p_line->i_width < i_width )
1013 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1015 i_align_offset = i_width - p_line->i_width;
1017 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1019 i_align_offset = ( i_width - p_line->i_width ) / 2;
1023 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1025 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1027 i_offset = ( p_line->p_glyph_pos[ i ].y +
1028 i_glyph_tmax - p_glyph->top + 3 ) *
1029 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1032 if( p_line->b_new_color_mode )
1034 /* Every glyph can (and in fact must) have its own color */
1035 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1038 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1040 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1042 uint8_t i_y_local = i_y;
1043 uint8_t i_u_local = i_u;
1044 uint8_t i_v_local = i_v;
1046 if( p_line->p_fg_bg_ratio != 0x00 )
1048 int i_split = p_glyph->bitmap.width *
1049 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1053 YUVFromRGB( p_line->p_bg_rgb[ i ],
1054 &i_y_local, &i_u_local, &i_v_local );
1058 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1060 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1061 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1063 p_dst_u[i_offset+x] = i_u;
1064 p_dst_v[i_offset+x] = i_v;
1066 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1067 p_dst_a[i_offset+x] = 0xff;
1070 i_offset += i_pitch;
1073 if( p_line->pi_underline_thickness[ i ] )
1075 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1076 p_line->pi_underline_offset[ i ],
1077 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1078 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1079 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1080 i_glyph_tmax, i_align_offset,
1087 /* Apply the alpha setting */
1088 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1089 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1095 * This function renders a text subpicture region into another one.
1096 * It also calculates the size needed for this string, and renders the
1097 * needed glyphs into memory. It is used as pf_add_string callback in
1098 * the vout method by this module
1100 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1101 subpicture_region_t *p_region_in )
1103 filter_sys_t *p_sys = p_filter->p_sys;
1104 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1105 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1106 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1107 int i_string_length;
1109 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1110 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1120 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1121 psz_string = p_region_in->psz_text;
1122 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1124 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1125 i_scale = val.i_int;
1127 if( p_region_in->p_style )
1129 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1130 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1131 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1135 i_font_color = p_sys->i_font_color;
1136 i_font_alpha = 255 - p_sys->i_font_opacity;
1137 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1140 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1141 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1142 SetFontSize( p_filter, i_font_size );
1144 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1145 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1146 i_blue = i_font_color & 0x000000FF;
1148 result.x = result.y = 0;
1149 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1151 psz_unicode = psz_unicode_orig =
1152 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1153 if( psz_unicode == NULL )
1155 #if defined(WORDS_BIGENDIAN)
1156 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1158 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1160 if( iconv_handle == (vlc_iconv_t)-1 )
1162 msg_Warn( p_filter, "unable to do conversion" );
1168 const char *p_in_buffer = psz_string;
1169 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1170 i_in_bytes = strlen( psz_string );
1171 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1172 i_out_bytes_left = i_out_bytes;
1173 p_out_buffer = (char *)psz_unicode;
1174 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1176 &p_out_buffer, &i_out_bytes_left );
1178 vlc_iconv_close( iconv_handle );
1182 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1183 "bytes left %u", (unsigned)i_in_bytes );
1186 *(uint32_t*)p_out_buffer = 0;
1187 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1190 #if defined(HAVE_FRIBIDI)
1192 uint32_t *p_fribidi_string;
1193 int32_t start_pos, pos = 0;
1195 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1196 if( !p_fribidi_string )
1199 /* Do bidi conversion line-by-line */
1200 while( pos < i_string_length )
1202 while( pos < i_string_length )
1204 i_char = psz_unicode[pos];
1205 if (i_char != '\r' && i_char != '\n')
1207 p_fribidi_string[pos] = i_char;
1211 while( pos < i_string_length )
1213 i_char = psz_unicode[pos];
1214 if (i_char == '\r' || i_char == '\n')
1218 if (pos > start_pos)
1220 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1221 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1224 (FriBidiChar*)p_fribidi_string + start_pos,
1229 free( psz_unicode_orig );
1230 psz_unicode = psz_unicode_orig = p_fribidi_string;
1231 p_fribidi_string[ i_string_length ] = 0;
1235 /* Calculate relative glyph positions and a bounding box for the
1237 if( !(p_line = NewLine( strlen( psz_string ))) )
1240 i_pen_x = i_pen_y = 0;
1242 psz_line_start = psz_unicode;
1244 #define face p_sys->p_face
1245 #define glyph face->glyph
1247 while( *psz_unicode )
1249 i_char = *psz_unicode++;
1250 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1255 if( i_char == '\n' )
1257 psz_line_start = psz_unicode;
1258 if( !(p_next = NewLine( strlen( psz_string ))) )
1260 p_line->p_next = p_next;
1261 p_line->i_width = line.xMax;
1262 p_line->i_height = face->size->metrics.height >> 6;
1263 p_line->pp_glyphs[ i ] = NULL;
1264 p_line->i_alpha = i_font_alpha;
1265 p_line->i_red = i_red;
1266 p_line->i_green = i_green;
1267 p_line->i_blue = i_blue;
1270 result.x = __MAX( result.x, line.xMax );
1271 result.y += face->size->metrics.height >> 6;
1274 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1275 i_pen_y += face->size->metrics.height >> 6;
1277 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1282 i_glyph_index = FT_Get_Char_Index( face, i_char );
1283 if( p_sys->i_use_kerning && i_glyph_index
1287 FT_Get_Kerning( face, i_previous, i_glyph_index,
1288 ft_kerning_default, &delta );
1289 i_pen_x += delta.x >> 6;
1292 p_line->p_glyph_pos[ i ].x = i_pen_x;
1293 p_line->p_glyph_pos[ i ].y = i_pen_y;
1294 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1297 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1301 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1304 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1308 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1309 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1312 FT_Done_Glyph( tmp_glyph );
1315 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1318 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1319 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1320 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1322 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1323 p_line->pp_glyphs[ i ] = NULL;
1325 p_line = NewLine( strlen( psz_string ));
1326 if( p_prev ) p_prev->p_next = p_line;
1327 else p_lines = p_line;
1329 uint32_t *psz_unicode_saved = psz_unicode;
1330 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1334 if( psz_unicode == psz_line_start )
1335 { /* try harder to break that line */
1336 psz_unicode = psz_unicode_saved;
1337 while( psz_unicode > psz_line_start &&
1338 *psz_unicode != '_' && *psz_unicode != '/' &&
1339 *psz_unicode != '\\' && *psz_unicode != '.' )
1344 if( psz_unicode == psz_line_start )
1346 msg_Warn( p_filter, "unbreakable string" );
1351 *psz_unicode = '\n';
1353 psz_unicode = psz_line_start;
1356 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1359 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1360 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1362 i_previous = i_glyph_index;
1363 i_pen_x += glyph->advance.x >> 6;
1367 p_line->i_width = line.xMax;
1368 p_line->i_height = face->size->metrics.height >> 6;
1369 p_line->pp_glyphs[ i ] = NULL;
1370 p_line->i_alpha = i_font_alpha;
1371 p_line->i_red = i_red;
1372 p_line->i_green = i_green;
1373 p_line->i_blue = i_blue;
1374 result.x = __MAX( result.x, line.xMax );
1375 result.y += line.yMax - line.yMin;
1380 p_region_out->i_x = p_region_in->i_x;
1381 p_region_out->i_y = p_region_in->i_y;
1383 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1384 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1386 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1388 free( psz_unicode_orig );
1389 FreeLines( p_lines );
1393 free( psz_unicode_orig );
1394 FreeLines( p_lines );
1395 return VLC_EGENERIC;
1398 #ifdef HAVE_FONTCONFIG
1399 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1400 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1401 bool b_italic, bool b_uline )
1403 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1407 p_style->i_font_size = i_font_size;
1408 p_style->i_font_color = i_font_color;
1409 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1410 p_style->b_italic = b_italic;
1411 p_style->b_bold = b_bold;
1412 p_style->b_underline = b_uline;
1414 p_style->psz_fontname = strdup( psz_fontname );
1419 static void DeleteStyle( ft_style_t *p_style )
1423 free( p_style->psz_fontname );
1428 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1435 if(( s1->i_font_size == s2->i_font_size ) &&
1436 ( s1->i_font_color == s2->i_font_color ) &&
1437 ( s1->b_italic == s2->b_italic ) &&
1438 ( s1->b_bold == s2->b_bold ) &&
1439 ( s1->b_underline == s2->b_underline ) &&
1440 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1447 static void IconvText( filter_t *p_filter, const char *psz_string,
1448 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1450 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1452 /* If memory hasn't been allocated for our output string, allocate it here
1453 * - the calling function must now be responsible for freeing it.
1455 if( !*ppsz_unicode )
1456 *ppsz_unicode = (uint32_t *)
1457 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1459 /* We don't need to handle a NULL pointer in *ppsz_unicode
1460 * if we are instead testing for a non NULL value like we are here */
1464 #if defined(WORDS_BIGENDIAN)
1465 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1467 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1469 if( iconv_handle != (vlc_iconv_t)-1 )
1471 char *p_in_buffer, *p_out_buffer;
1472 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1473 i_in_bytes = strlen( psz_string );
1474 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1475 i_out_bytes_left = i_out_bytes;
1476 p_in_buffer = (char *) psz_string;
1477 p_out_buffer = (char *) *ppsz_unicode;
1478 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1479 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1481 vlc_iconv_close( iconv_handle );
1485 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1486 "bytes left %u", (unsigned)i_in_bytes );
1490 *(uint32_t*)p_out_buffer = 0;
1492 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1497 msg_Warn( p_filter, "unable to do conversion" );
1502 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1503 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1506 ft_style_t *p_style = NULL;
1508 char *psz_fontname = NULL;
1509 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1510 uint32_t i_karaoke_bg_color = i_font_color;
1511 int i_font_size = p_sys->i_font_size;
1513 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1514 &i_font_color, &i_karaoke_bg_color ))
1516 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1517 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1522 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1523 bool b_uline, int i_karaoke_bgcolor,
1524 line_desc_t *p_line, uint32_t *psz_unicode,
1525 int *pi_pen_x, int i_pen_y, int *pi_start,
1526 FT_Vector *p_result )
1531 bool b_first_on_line = true;
1534 int i_pen_x_start = *pi_pen_x;
1536 uint32_t *psz_unicode_start = psz_unicode;
1538 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1540 /* Account for part of line already in position */
1541 for( i=0; i<*pi_start; i++ )
1545 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1546 ft_glyph_bbox_pixels, &glyph_size );
1548 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1549 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1550 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1551 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1557 b_first_on_line = false;
1559 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1565 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1566 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1570 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1571 ft_kerning_default, &delta );
1572 *pi_pen_x += delta.x >> 6;
1574 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1575 p_line->p_glyph_pos[ i ].y = i_pen_y;
1577 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1581 "unable to render text FT_Load_Glyph returned %d", i_error );
1582 p_line->pp_glyphs[ i ] = NULL;
1583 return VLC_EGENERIC;
1585 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1589 "unable to render text FT_Get_Glyph returned %d", i_error );
1590 p_line->pp_glyphs[ i ] = NULL;
1591 return VLC_EGENERIC;
1593 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1594 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1597 FT_Done_Glyph( tmp_glyph );
1602 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1603 p_face->size->metrics.y_scale));
1604 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1605 p_face->size->metrics.y_scale));
1607 p_line->pi_underline_offset[ i ] =
1608 ( aOffset < 0 ) ? -aOffset : aOffset;
1609 p_line->pi_underline_thickness[ i ] =
1610 ( aSize < 0 ) ? -aSize : aSize;
1612 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1613 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1614 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1615 p_line->p_fg_bg_ratio[ i ] = 0x00;
1617 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1618 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1619 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1621 for( ; i >= *pi_start; i-- )
1622 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1625 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1629 if( psz_unicode == psz_unicode_start )
1631 if( b_first_on_line )
1633 msg_Warn( p_filter, "unbreakable string" );
1634 p_line->pp_glyphs[ i ] = NULL;
1635 return VLC_EGENERIC;
1637 *pi_pen_x = i_pen_x_start;
1639 p_line->i_width = line.xMax;
1640 p_line->i_height = __MAX( p_line->i_height,
1641 p_face->size->metrics.height >> 6 );
1642 p_line->pp_glyphs[ i ] = NULL;
1644 p_result->x = __MAX( p_result->x, line.xMax );
1645 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1646 i_yMax - i_yMin ) );
1651 *psz_unicode = '\n';
1653 psz_unicode = psz_unicode_start;
1654 *pi_pen_x = i_pen_x_start;
1662 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1663 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1665 i_previous = i_glyph_index;
1666 *pi_pen_x += p_face->glyph->advance.x >> 6;
1669 p_line->i_width = line.xMax;
1670 p_line->i_height = __MAX( p_line->i_height,
1671 p_face->size->metrics.height >> 6 );
1672 p_line->pp_glyphs[ i ] = NULL;
1674 p_result->x = __MAX( p_result->x, line.xMax );
1675 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1676 line.yMax - line.yMin ) );
1680 /* Get rid of any text processed - if necessary repositioning
1681 * at the start of a new line of text
1685 *psz_unicode_start = '\0';
1687 else if( psz_unicode > psz_unicode_start )
1689 for( i=0; psz_unicode[ i ]; i++ )
1690 psz_unicode_start[ i ] = psz_unicode[ i ];
1691 psz_unicode_start[ i ] = '\0';
1697 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1698 uint32_t **psz_text_out, uint32_t *pi_runs,
1699 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1700 ft_style_t *p_style )
1702 uint32_t i_string_length = 0;
1704 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1705 *psz_text_out += i_string_length;
1707 if( ppp_styles && ppi_run_lengths )
1713 *ppp_styles = (ft_style_t **)
1714 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1716 else if( *pi_runs == 1 )
1718 *ppp_styles = (ft_style_t **)
1719 malloc( *pi_runs * sizeof( ft_style_t * ) );
1722 /* We have just malloc'ed this memory successfully -
1723 * *pi_runs HAS to be within the memory area of *ppp_styles */
1726 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1730 if( *ppi_run_lengths )
1732 *ppi_run_lengths = (uint32_t *)
1733 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1735 else if( *pi_runs == 1 )
1737 *ppi_run_lengths = (uint32_t *)
1738 malloc( *pi_runs * sizeof( uint32_t ) );
1741 /* same remarks here */
1742 if( *ppi_run_lengths )
1744 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1747 /* If we couldn't use the p_style argument due to memory allocation
1748 * problems above, release it here.
1750 if( p_style ) DeleteStyle( p_style );
1753 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1757 for( k=0; k < p_sys->i_font_attachments; k++ )
1759 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1761 FT_Face p_face = NULL;
1763 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1771 bool match = !strcasecmp( p_face->family_name,
1772 p_style->psz_fontname );
1774 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1775 match = match && p_style->b_bold;
1777 match = match && !p_style->b_bold;
1779 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1780 match = match && p_style->b_italic;
1782 match = match && !p_style->b_italic;
1790 FT_Done_Face( p_face );
1795 return VLC_EGENERIC;
1798 static int ProcessLines( filter_t *p_filter,
1803 uint32_t *pi_run_lengths,
1804 ft_style_t **pp_styles,
1805 line_desc_t **pp_lines,
1807 FT_Vector *p_result,
1811 uint32_t *pi_k_run_lengths,
1812 uint32_t *pi_k_durations )
1814 filter_sys_t *p_sys = p_filter->p_sys;
1815 ft_style_t **pp_char_styles;
1816 int *p_new_positions = NULL;
1817 int8_t *p_levels = NULL;
1818 uint8_t *pi_karaoke_bar = NULL;
1822 /* Assign each character in the text string its style explicitly, so that
1823 * after the characters have been shuffled around by Fribidi, we can re-apply
1824 * the styles, and to simplify the calculation of runs within a line.
1826 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1827 if( !pp_char_styles )
1832 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1833 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1834 * we just won't be able to display the progress bar; at least we'll
1840 for( j = 0; j < i_runs; j++ )
1841 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1842 pp_char_styles[ i++ ] = pp_styles[ j ];
1844 #if defined(HAVE_FRIBIDI)
1846 ft_style_t **pp_char_styles_new;
1847 int *p_old_positions;
1848 uint32_t *p_fribidi_string;
1849 int start_pos, pos = 0;
1851 pp_char_styles_new = (ft_style_t **)
1852 malloc( i_len * sizeof( ft_style_t * ));
1854 p_fribidi_string = (uint32_t *)
1855 malloc( (i_len + 1) * sizeof(uint32_t) );
1856 p_old_positions = (int *)
1857 malloc( (i_len + 1) * sizeof( int ) );
1858 p_new_positions = (int *)
1859 malloc( (i_len + 1) * sizeof( int ) );
1860 p_levels = (int8_t *)
1861 malloc( (i_len + 1) * sizeof( int8_t ) );
1863 if( ! pp_char_styles_new ||
1864 ! p_fribidi_string ||
1865 ! p_old_positions ||
1866 ! p_new_positions ||
1870 free( p_old_positions );
1871 free( p_new_positions );
1872 free( p_fribidi_string );
1873 free( pp_char_styles_new );
1874 free( pi_karaoke_bar );
1876 free( pp_char_styles );
1880 /* Do bidi conversion line-by-line */
1883 while(pos < i_len) {
1884 if (psz_text[pos] != '\n')
1886 p_fribidi_string[pos] = psz_text[pos];
1887 pp_char_styles_new[pos] = pp_char_styles[pos];
1888 p_new_positions[pos] = pos;
1893 while(pos < i_len) {
1894 if (psz_text[pos] == '\n')
1898 if (pos > start_pos)
1900 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1901 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1902 pos - start_pos, &base_dir,
1903 (FriBidiChar*)p_fribidi_string + start_pos,
1904 p_new_positions + start_pos,
1906 p_levels + start_pos );
1907 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1909 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1910 p_old_positions[ j - start_pos ] ];
1911 p_new_positions[ j ] += start_pos;
1915 free( p_old_positions );
1916 free( pp_char_styles );
1917 pp_char_styles = pp_char_styles_new;
1918 psz_text = p_fribidi_string;
1919 p_fribidi_string[ i_len ] = 0;
1922 /* Work out the karaoke */
1923 if( pi_karaoke_bar )
1925 int64_t i_last_duration = 0;
1926 int64_t i_duration = 0;
1927 int64_t i_start_pos = 0;
1928 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1930 for( k = 0; k< i_k_runs; k++ )
1932 double fraction = 0.0;
1934 i_duration += pi_k_durations[ k ];
1936 if( i_duration < i_elapsed )
1938 /* Completely finished this run-length -
1939 * let it render normally */
1943 else if( i_elapsed < i_last_duration )
1945 /* Haven't got up to this segment yet -
1946 * render it completely in karaoke BG mode */
1952 /* Partway through this run */
1954 fraction = (double)(i_elapsed - i_last_duration) /
1955 (double)pi_k_durations[ k ];
1957 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1959 double shade = pi_k_run_lengths[ k ] * fraction;
1961 if( p_new_positions )
1962 j = p_new_positions[ i_start_pos + i ];
1964 j = i_start_pos + i;
1966 if( i < (uint32_t)shade )
1967 pi_karaoke_bar[ j ] = 0xff;
1968 else if( (double)i > shade )
1969 pi_karaoke_bar[ j ] = 0x00;
1972 shade -= (int)shade;
1973 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1974 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1978 i_last_duration = i_duration;
1979 i_start_pos += pi_k_run_lengths[ k ];
1983 free( p_new_positions );
1985 FT_Vector tmp_result;
1987 line_desc_t *p_line = NULL;
1988 line_desc_t *p_prev = NULL;
1994 p_result->x = p_result->y = 0;
1995 tmp_result.x = tmp_result.y = 0;
1998 for( k = 0; k <= (uint32_t) i_len; k++ )
2000 if( ( k == (uint32_t) i_len ) ||
2002 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2004 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2006 /* End of the current style run */
2007 FT_Face p_face = NULL;
2010 /* Look for a match amongst our attachments first */
2011 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2015 char *psz_fontfile = NULL;
2017 vlc_mutex_lock( &fb_lock );
2018 if( p_sys->b_fontconfig_ok )
2020 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2021 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2022 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2023 p_style->psz_fontname,
2028 vlc_mutex_unlock( &fb_lock );
2030 if( psz_fontfile && ! *psz_fontfile )
2032 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2033 " so using default font", p_style->psz_fontname,
2034 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2035 (p_style->b_bold ? "(Bold)" :
2036 (p_style->b_italic ? "(Italic)" : ""))) );
2037 free( psz_fontfile );
2038 psz_fontfile = NULL;
2043 if( FT_New_Face( p_sys->p_library,
2044 psz_fontfile, i_idx, &p_face ) )
2046 free( psz_fontfile );
2047 free( pp_char_styles );
2048 #if defined(HAVE_FRIBIDI)
2051 free( pi_karaoke_bar );
2052 return VLC_EGENERIC;
2054 free( psz_fontfile );
2058 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2060 /* We've loaded a font face which is unhelpful for actually
2061 * rendering text - fallback to the default one.
2063 FT_Done_Face( p_face );
2067 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2068 ft_encoding_unicode ) ||
2069 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2070 p_style->i_font_size ) )
2072 if( p_face ) FT_Done_Face( p_face );
2073 free( pp_char_styles );
2074 #if defined(HAVE_FRIBIDI)
2077 free( pi_karaoke_bar );
2078 return VLC_EGENERIC;
2080 p_sys->i_use_kerning =
2081 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2084 uint32_t *psz_unicode = (uint32_t *)
2085 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2088 if( p_face ) FT_Done_Face( p_face );
2089 free( pp_char_styles );
2090 free( psz_unicode );
2091 #if defined(HAVE_FRIBIDI)
2094 free( pi_karaoke_bar );
2097 memcpy( psz_unicode, psz_text + i_prev,
2098 sizeof( uint32_t ) * ( k - i_prev ) );
2099 psz_unicode[ k - i_prev ] = 0;
2100 while( *psz_unicode )
2104 if( !(p_line = NewLine( i_len - i_prev)) )
2106 if( p_face ) FT_Done_Face( p_face );
2107 free( pp_char_styles );
2108 free( psz_unicode );
2109 #if defined(HAVE_FRIBIDI)
2112 free( pi_karaoke_bar );
2115 /* New Color mode only works in YUVA rendering mode --
2116 * (RGB mode has palette constraints on it). We therefore
2117 * need to populate the legacy colour fields also.
2119 p_line->b_new_color_mode = true;
2120 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2121 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2122 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2123 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2124 p_line->p_next = NULL;
2126 i_pen_y += tmp_result.y;
2130 if( p_prev ) p_prev->p_next = p_line;
2131 else *pp_lines = p_line;
2134 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2135 p_style->i_font_color, p_style->b_underline,
2136 p_style->i_karaoke_bg_color,
2137 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2138 &tmp_result ) != VLC_SUCCESS )
2140 if( p_face ) FT_Done_Face( p_face );
2141 free( pp_char_styles );
2142 free( psz_unicode );
2143 #if defined(HAVE_FRIBIDI)
2146 free( pi_karaoke_bar );
2147 return VLC_EGENERIC;
2152 p_result->x = __MAX( p_result->x, tmp_result.x );
2153 p_result->y += tmp_result.y;
2158 if( *psz_unicode == '\n')
2162 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2164 *c_ptr = *(c_ptr+1);
2169 free( psz_unicode );
2170 if( p_face ) FT_Done_Face( p_face );
2174 free( pp_char_styles );
2175 #if defined(HAVE_FRIBIDI)
2180 p_result->x = __MAX( p_result->x, tmp_result.x );
2181 p_result->y += tmp_result.y;
2184 if( pi_karaoke_bar )
2187 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2189 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2191 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2195 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2197 /* 100% BG colour will render faster if we
2198 * instead make it 100% FG colour, so leave
2199 * the ratio alone and copy the value across
2201 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2205 if( pi_karaoke_bar[ i ] & 0x80 )
2207 /* Swap Left and Right sides over for Right aligned
2208 * language text (eg. Arabic, Hebrew)
2210 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2212 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2213 p_line->p_bg_rgb[ k ] = i_tmp;
2215 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2218 /* Jump over the '\n' at the line-end */
2221 free( pi_karaoke_bar );
2227 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2228 subpicture_region_t *p_region_in )
2230 int rv = VLC_SUCCESS;
2231 stream_t *p_sub = NULL;
2232 xml_t *p_xml = NULL;
2233 xml_reader_t *p_xml_reader = NULL;
2235 if( !p_region_in || !p_region_in->psz_html )
2236 return VLC_EGENERIC;
2238 /* Reset the default fontsize in case screen metrics have changed */
2239 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2241 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2242 (uint8_t *) p_region_in->psz_html,
2243 strlen( p_region_in->psz_html ),
2247 p_xml = xml_Create( p_filter );
2250 bool b_karaoke = false;
2252 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2255 /* Look for Root Node */
2256 if( xml_ReaderRead( p_xml_reader ) == 1 )
2258 char *psz_node = xml_ReaderName( p_xml_reader );
2260 if( !strcasecmp( "karaoke", psz_node ) )
2262 /* We're going to have to render the text a number
2263 * of times to show the progress marker on the text.
2265 var_SetBool( p_filter, "text-rerender", true );
2268 else if( !strcasecmp( "text", psz_node ) )
2274 /* Only text and karaoke tags are supported */
2275 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2276 xml_ReaderDelete( p_xml, p_xml_reader );
2277 p_xml_reader = NULL;
2289 uint32_t i_runs = 0;
2290 uint32_t i_k_runs = 0;
2291 uint32_t *pi_run_lengths = NULL;
2292 uint32_t *pi_k_run_lengths = NULL;
2293 uint32_t *pi_k_durations = NULL;
2294 ft_style_t **pp_styles = NULL;
2296 line_desc_t *p_lines = NULL;
2298 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2299 sizeof( uint32_t ) );
2304 rv = ProcessNodes( p_filter, p_xml_reader,
2305 p_region_in->p_style, psz_text, &i_len,
2306 &i_runs, &pi_run_lengths, &pp_styles,
2308 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2311 p_region_out->i_x = p_region_in->i_x;
2312 p_region_out->i_y = p_region_in->i_y;
2314 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2316 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2317 pi_run_lengths, pp_styles, &p_lines, &result,
2318 b_karaoke, i_k_runs, pi_k_run_lengths,
2322 for( k=0; k<i_runs; k++)
2323 DeleteStyle( pp_styles[k] );
2325 free( pi_run_lengths );
2328 /* Don't attempt to render text that couldn't be layed out
2331 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2333 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2335 Render( p_filter, p_region_out, p_lines,
2336 result.x, result.y );
2340 RenderYUVA( p_filter, p_region_out, p_lines,
2341 result.x, result.y );
2345 FreeLines( p_lines );
2347 xml_ReaderDelete( p_xml, p_xml_reader );
2349 xml_Delete( p_xml );
2351 stream_Delete( p_sub );
2357 static char* FontConfig_Select( FcConfig* priv, const char* family,
2358 bool b_bold, bool b_italic, int *i_idx )
2361 FcPattern *pat, *p_pat;
2365 pat = FcPatternCreate();
2366 if (!pat) return NULL;
2368 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2369 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2370 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2371 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2373 FcDefaultSubstitute( pat );
2375 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2377 FcPatternDestroy( pat );
2381 p_pat = FcFontMatch( priv, pat, &result );
2382 FcPatternDestroy( pat );
2383 if( !p_pat ) return NULL;
2385 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2386 || ( val_b != FcTrue ) )
2388 FcPatternDestroy( p_pat );
2391 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2396 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2398 FcPatternDestroy( p_pat );
2403 if( strcasecmp((const char*)val_s, family ) != 0 )
2404 msg_Warn( p_filter, "fontconfig: selected font family is not"
2405 "the requested one: '%s' != '%s'\n",
2406 (const char*)val_s, family );
2409 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2411 FcPatternDestroy( p_pat );
2415 FcPatternDestroy( p_pat );
2416 return strdup( (const char*)val_s );
2420 static void FreeLine( line_desc_t *p_line )
2423 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2425 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2427 free( p_line->pp_glyphs );
2428 free( p_line->p_glyph_pos );
2429 free( p_line->p_fg_rgb );
2430 free( p_line->p_bg_rgb );
2431 free( p_line->p_fg_bg_ratio );
2432 free( p_line->pi_underline_offset );
2433 free( p_line->pi_underline_thickness );
2437 static void FreeLines( line_desc_t *p_lines )
2439 line_desc_t *p_line, *p_next;
2441 if( !p_lines ) return;
2443 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2445 p_next = p_line->p_next;
2450 static line_desc_t *NewLine( int i_count )
2452 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2454 if( !p_line ) return NULL;
2455 p_line->i_height = 0;
2456 p_line->i_width = 0;
2457 p_line->p_next = NULL;
2459 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2460 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2461 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2462 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2463 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2464 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2465 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2466 if( ( p_line->pp_glyphs == NULL ) ||
2467 ( p_line->p_glyph_pos == NULL ) ||
2468 ( p_line->p_fg_rgb == NULL ) ||
2469 ( p_line->p_bg_rgb == NULL ) ||
2470 ( p_line->p_fg_bg_ratio == NULL ) ||
2471 ( p_line->pi_underline_offset == NULL ) ||
2472 ( p_line->pi_underline_thickness == NULL ) )
2474 free( p_line->pi_underline_thickness );
2475 free( p_line->pi_underline_offset );
2476 free( p_line->p_fg_rgb );
2477 free( p_line->p_bg_rgb );
2478 free( p_line->p_fg_bg_ratio );
2479 free( p_line->p_glyph_pos );
2480 free( p_line->pp_glyphs );
2484 p_line->pp_glyphs[0] = NULL;
2485 p_line->b_new_color_mode = false;
2490 static int GetFontSize( filter_t *p_filter )
2492 filter_sys_t *p_sys = p_filter->p_sys;
2496 if( p_sys->i_default_font_size )
2498 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2499 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2501 i_size = p_sys->i_default_font_size;
2505 var_Get( p_filter, "freetype-rel-fontsize", &val );
2508 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2509 p_filter->p_sys->i_display_height =
2510 p_filter->fmt_out.video.i_height;
2515 msg_Warn( p_filter, "invalid fontsize, using 12" );
2516 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2517 i_size = 12 * val.i_int / 1000;
2524 static int SetFontSize( filter_t *p_filter, int i_size )
2526 filter_sys_t *p_sys = p_filter->p_sys;
2530 i_size = GetFontSize( p_filter );
2532 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2535 p_sys->i_font_size = i_size;
2537 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2539 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2540 return VLC_EGENERIC;
2546 static void YUVFromRGB( uint32_t i_argb,
2547 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2549 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2550 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2551 int i_blue = ( i_argb & 0x000000ff );
2553 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2554 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2555 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2556 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2557 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2558 -585 * i_blue + 4096 + 1048576) >> 13, 240);