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>
74 #define DEFAULT_FONT FC_DEFAULT_FONT
79 /*****************************************************************************
81 *****************************************************************************/
82 static int Create ( vlc_object_t * );
83 static void Destroy( vlc_object_t * );
85 #define FONT_TEXT N_("Font")
87 #ifdef HAVE_FONTCONFIG
88 #define FONT_LONGTEXT N_("Font family for the font you want to use")
90 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
93 #define FONTSIZE_TEXT N_("Font size in pixels")
94 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
95 "that will be rendered on the video. " \
96 "If set to something different than 0 this option will override the " \
97 "relative font size." )
98 #define OPACITY_TEXT N_("Opacity")
99 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
100 "text that will be rendered on the video. 0 = transparent, " \
101 "255 = totally opaque. " )
102 #define COLOR_TEXT N_("Text default color")
103 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
104 "the video. This must be an hexadecimal (like HTML colors). The first two "\
105 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
106 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
107 #define FONTSIZER_TEXT N_("Relative font size")
108 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
109 "fonts that will be rendered on the video. If absolute font size is set, "\
110 "relative size will be overriden." )
112 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
113 static const char *const ppsz_sizes_text[] = {
114 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
115 #define YUVP_TEXT N_("Use YUVP renderer")
116 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
117 "This option is only needed if you want to encode into DVB subtitles" )
118 #define EFFECT_TEXT N_("Font Effect")
119 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
120 "text to improve its readability." )
122 #define EFFECT_BACKGROUND 1
123 #define EFFECT_OUTLINE 2
124 #define EFFECT_OUTLINE_FAT 3
126 static int const pi_effects[] = { 1, 2, 3 };
127 static const char *const ppsz_effects_text[] = {
128 N_("Background"),N_("Outline"), N_("Fat Outline") };
129 static const int pi_color_values[] = {
130 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
131 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
132 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
134 static const char *const ppsz_color_descriptions[] = {
135 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
136 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
137 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
140 set_shortname( N_("Text renderer"))
141 set_description( N_("Freetype2 font renderer") )
142 set_category( CAT_VIDEO )
143 set_subcategory( SUBCAT_VIDEO_SUBPIC )
145 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
148 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
149 FONTSIZE_LONGTEXT, true )
151 /* opacity valid on 0..255, with default 255 = fully opaque */
152 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
153 OPACITY_TEXT, OPACITY_LONGTEXT, true )
155 /* hook to the color values list, with default 0x00ffffff = white */
156 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
157 COLOR_LONGTEXT, false )
158 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
160 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
161 FONTSIZER_LONGTEXT, false )
162 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
163 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
164 EFFECT_LONGTEXT, false )
165 change_integer_list( pi_effects, ppsz_effects_text, NULL )
167 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
168 YUVP_LONGTEXT, true )
169 set_capability( "text renderer", 100 )
170 add_shortcut( "text" )
171 set_callbacks( Create, Destroy )
176 /*****************************************************************************
178 *****************************************************************************/
180 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
181 static int RenderText( filter_t *, subpicture_region_t *,
182 subpicture_region_t * );
183 #ifdef HAVE_FONTCONFIG
184 static int RenderHtml( filter_t *, subpicture_region_t *,
185 subpicture_region_t * );
186 static char *FontConfig_Select( FcConfig *, const char *,
191 static int LoadFontsFromAttachments( filter_t *p_filter );
193 static int GetFontSize( filter_t *p_filter );
194 static int SetFontSize( filter_t *, int );
195 static void YUVFromRGB( uint32_t i_argb,
196 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
198 typedef struct line_desc_t line_desc_t;
201 /** NULL-terminated list of glyphs making the string */
202 FT_BitmapGlyph *pp_glyphs;
203 /** list of relative positions for the glyphs */
204 FT_Vector *p_glyph_pos;
205 /** list of RGB information for styled text
206 * -- if the rendering mode supports it (RenderYUVA) and
207 * b_new_color_mode is set, then it becomes possible to
208 * have multicoloured text within the subtitles. */
211 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
212 bool b_new_color_mode;
213 /** underline information -- only supplied if text should be underlined */
214 uint16_t *pi_underline_offset;
215 uint16_t *pi_underline_thickness;
219 int i_red, i_green, i_blue;
224 static line_desc_t *NewLine( int );
229 uint32_t i_font_color; /* ARGB */
230 uint32_t i_karaoke_bg_color; /* ARGB */
237 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
238 static void FreeLines( line_desc_t * );
239 static void FreeLine( line_desc_t * );
241 #ifdef HAVE_FONTCONFIG
242 static vlc_object_t *FontBuilderAttach( filter_t *p_filter );
243 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
244 static void* FontBuilderThread( vlc_object_t *p_this);
245 static void FontBuilderDestructor( vlc_object_t *p_this );
246 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
247 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
251 /*****************************************************************************
252 * filter_sys_t: freetype local data
253 *****************************************************************************
254 * This structure is part of the video output thread descriptor.
255 * It describes the freetype specific properties of an output thread.
256 *****************************************************************************/
259 FT_Library p_library; /* handle to library */
260 FT_Face p_face; /* handle to face object */
262 uint8_t i_font_opacity;
267 int i_default_font_size;
268 int i_display_height;
269 #ifdef HAVE_FONTCONFIG
270 char* psz_fontfamily;
271 bool b_fontconfig_ok;
272 FcConfig *p_fontconfig;
276 input_attachment_t **pp_font_attachments;
277 int i_font_attachments;
279 vlc_object_t *p_fontbuilder;
282 #define UCHAR uint32_t
283 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
284 #define TR_FONT_STYLE_PTR ft_style_t *
286 #include "text_renderer.h"
288 /*****************************************************************************
289 * Create: allocates osd-text video thread output method
290 *****************************************************************************
291 * This function allocates and initializes a Clone vout method.
292 *****************************************************************************/
293 static int Create( vlc_object_t *p_this )
295 filter_t *p_filter = (filter_t *)p_this;
297 char *psz_fontfile=NULL;
298 char *psz_fontfamily=NULL;
299 int i_error,fontindex;
301 #ifdef HAVE_FONTCONFIG
302 FcPattern *fontpattern = NULL, *fontmatch = NULL;
303 /* Initialise result to Match, as fontconfig doesnt
304 * really set this other than some error-cases */
305 FcResult fontresult = FcResultMatch;
309 /* Allocate structure */
310 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
313 #ifdef HAVE_FONTCONFIG
314 p_sys->psz_fontfamily = NULL;
318 p_sys->p_library = 0;
319 p_sys->i_font_size = 0;
320 p_sys->i_display_height = 0;
322 var_Create( p_filter, "freetype-rel-fontsize",
323 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
325 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
326 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
327 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
328 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
329 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
330 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
331 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
334 if( !psz_fontfamily || !*psz_fontfamily )
336 #ifdef HAVE_FONTCONFIG
337 free( psz_fontfamily);
338 psz_fontfamily=strdup( DEFAULT_FONT );
340 free( psz_fontfamily );
341 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
342 if( !psz_fontfamily )
345 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
346 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
348 strcpy( psz_fontfamily, DEFAULT_FONT );
350 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
354 #ifdef HAVE_FONTCONFIG
355 /* Lets find some fontfile from freetype-font variable family */
357 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
360 fontpattern = FcPatternCreate();
365 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
366 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
367 free( psz_fontsize );
369 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
371 FcDefaultSubstitute( fontpattern );
373 /* testing fontresult here doesn't do any good really, but maybe it will
374 * in future as fontconfig code doesn't set it in all cases and just
375 * returns NULL or doesn't set to to Match on all Match cases.*/
376 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
377 if( !fontmatch || fontresult == FcResultNoMatch )
380 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
381 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
384 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
385 p_sys->psz_fontfamily = strdup( psz_fontfamily );
387 p_sys->psz_fontfamily = strdup( DEFAULT_FONT )
388 psz_fontfile = psz_fontfamily;
391 i_error = FT_Init_FreeType( &p_sys->p_library );
394 msg_Err( p_filter, "couldn't initialize freetype" );
398 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
399 fontindex, &p_sys->p_face );
401 if( i_error == FT_Err_Unknown_File_Format )
403 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
408 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
412 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
415 msg_Err( p_filter, "font has no unicode translation table" );
419 #ifdef HAVE_FONTCONFIG
420 p_sys->b_fontconfig_ok = false;
421 p_sys->p_fontconfig = NULL;
422 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
425 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
427 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
430 p_sys->pp_font_attachments = NULL;
431 p_sys->i_font_attachments = 0;
433 p_filter->pf_render_text = RenderText;
434 #ifdef HAVE_FONTCONFIG
435 p_filter->pf_render_html = RenderHtml;
436 FcPatternDestroy( fontmatch );
437 FcPatternDestroy( fontpattern );
439 p_filter->pf_render_html = NULL;
442 free( psz_fontfamily );
443 LoadFontsFromAttachments( p_filter );
448 #ifdef HAVE_FONTCONFIG
449 if( fontmatch ) FcPatternDestroy( fontmatch );
450 if( fontpattern ) FcPatternDestroy( fontpattern );
452 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
453 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
454 free( psz_fontfamily );
459 /*****************************************************************************
460 * Destroy: destroy Clone video thread output method
461 *****************************************************************************
462 * Clean up all data and library connections
463 *****************************************************************************/
464 static void Destroy( vlc_object_t *p_this )
466 filter_t *p_filter = (filter_t *)p_this;
467 filter_sys_t *p_sys = p_filter->p_sys;
469 if( p_sys->pp_font_attachments )
473 for( k = 0; k < p_sys->i_font_attachments; k++ )
474 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
476 free( p_sys->pp_font_attachments );
479 #ifdef HAVE_FONTCONFIG
480 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
481 xml_Delete( p_sys->p_xml );
482 free( p_sys->psz_fontfamily );
485 /* FcFini asserts calling the subfunction FcCacheFini()
486 * even if no other library functions have been made since FcInit(),
487 * so don't call it. */
489 FT_Done_Face( p_sys->p_face );
490 FT_Done_FreeType( p_sys->p_library );
494 #ifdef HAVE_FONTCONFIG
495 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
497 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
499 /* Check for an existing Fontbuilder thread */
500 vlc_mutex_lock( &fb_lock );
501 vlc_object_t *p_fontbuilder =
502 vlc_object_find_name( p_filter->p_libvlc,
503 "fontlist builder", FIND_CHILD );
507 /* Create the FontBuilderThread thread as a child of a top-level
508 * object, so that it can survive the destruction of the
509 * freetype object - the fontlist only needs to be built once,
510 * and calling the fontbuild a second time while the first is
511 * still in progress can cause thread instabilities.
513 * XXX The fontbuilder will be destroy as soon as it is unused.
516 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
517 sizeof(vlc_object_t) );
520 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
521 p_fontbuilder->p_private = NULL;
522 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
524 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
526 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
527 var_SetBool( p_fontbuilder, "build-done", false );
528 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
529 var_SetBool( p_fontbuilder, "build-joined", false );
531 if( vlc_thread_create( p_fontbuilder,
534 VLC_THREAD_PRIORITY_LOW ) )
536 msg_Warn( p_filter, "fontconfig database builder thread can't "
537 "be launched. Font styling support will be limited." );
544 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
545 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
547 vlc_mutex_unlock( &fb_lock );
548 return p_fontbuilder;
550 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
552 vlc_mutex_lock( &fb_lock );
555 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
557 /* We wait for the thread on the first FontBuilderDetach */
558 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
560 var_SetBool( p_fontbuilder, "build-joined", true );
561 vlc_mutex_unlock( &fb_lock );
562 /* We need to unlock otherwise we may not join (the thread waiting
563 * for the lock). It is safe to unlock as no one else will try a
564 * join and we have a reference on the object) */
565 vlc_thread_join( p_fontbuilder );
566 vlc_mutex_lock( &fb_lock );
568 vlc_object_release( p_fontbuilder );
570 vlc_mutex_unlock( &fb_lock );
572 static void* FontBuilderThread( vlc_object_t *p_this )
574 FcConfig *p_fontconfig = FcInitLoadConfig();
579 int canc = vlc_savecancel ();
581 //msg_Dbg( p_this, "Building font database..." );
582 msg_Dbg( p_this, "Building font database..." );
584 if(! FcConfigBuildFonts( p_fontconfig ))
586 /* Don't destroy the fontconfig object - we won't be able to do
587 * italics or bold or change the font face, but we will still
588 * be able to do underline and change the font size.
590 msg_Err( p_this, "fontconfig database can't be built. "
591 "Font styling won't be available" );
595 msg_Dbg( p_this, "Finished building font database." );
596 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
598 vlc_mutex_lock( &fb_lock );
599 p_this->p_private = p_fontconfig;
600 vlc_mutex_unlock( &fb_lock );
602 var_SetBool( p_this, "build-done", true );
603 vlc_restorecancel (canc);
607 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
609 filter_sys_t *p_sys = p_filter->p_sys;
611 p_sys->p_fontconfig = p_fontbuilder->p_private;
612 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
614 static void FontBuilderDestructor( vlc_object_t *p_this )
616 FcConfig *p_fontconfig = p_this->p_private;
619 FcConfigDestroy( p_fontconfig );
621 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
622 vlc_value_t oldval, vlc_value_t newval, void *param )
624 filter_t *p_filter = param;
628 vlc_mutex_lock( &fb_lock );
630 FontBuilderGetFcConfig( p_filter, p_this );
632 vlc_mutex_unlock( &fb_lock );
641 /*****************************************************************************
642 * Make any TTF/OTF fonts present in the attachments of the media file
643 * and store them for later use by the FreeType Engine
644 *****************************************************************************/
645 static int LoadFontsFromAttachments( filter_t *p_filter )
647 filter_sys_t *p_sys = p_filter->p_sys;
648 input_thread_t *p_input;
649 input_attachment_t **pp_attachments;
650 int i_attachments_cnt;
652 int rv = VLC_SUCCESS;
654 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
658 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
660 vlc_object_release(p_input);
664 p_sys->i_font_attachments = 0;
665 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
666 if(! p_sys->pp_font_attachments )
669 for( k = 0; k < i_attachments_cnt; k++ )
671 input_attachment_t *p_attach = pp_attachments[k];
673 if( p_sys->pp_font_attachments )
675 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
676 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
677 ( p_attach->i_data > 0 ) &&
678 ( p_attach->p_data != NULL ) )
680 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
684 vlc_input_attachment_Delete( p_attach );
689 vlc_input_attachment_Delete( p_attach );
692 free( pp_attachments );
694 vlc_object_release(p_input);
699 /*****************************************************************************
700 * Render: place string in picture
701 *****************************************************************************
702 * This function merges the previously rendered freetype glyphs into a picture
703 *****************************************************************************/
704 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
705 line_desc_t *p_line, int i_width, int i_height )
707 static const uint8_t pi_gamma[16] =
708 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
709 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
713 int i, x, y, i_pitch;
714 uint8_t i_y; /* YUV values, derived from incoming RGB */
717 /* Create a new subpicture region */
718 memset( &fmt, 0, sizeof(video_format_t) );
719 fmt.i_chroma = VLC_CODEC_YUVP;
721 fmt.i_width = fmt.i_visible_width = i_width + 4;
722 fmt.i_height = fmt.i_visible_height = i_height + 4;
723 if( p_region->fmt.i_visible_width > 0 )
724 fmt.i_visible_width = p_region->fmt.i_visible_width;
725 if( p_region->fmt.i_visible_height > 0 )
726 fmt.i_visible_height = p_region->fmt.i_visible_height;
727 fmt.i_x_offset = fmt.i_y_offset = 0;
729 assert( !p_region->p_picture );
730 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
731 if( !p_region->p_picture )
735 /* Calculate text color components */
736 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
737 25 * p_line->i_blue + 128) >> 8) + 16;
738 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
739 112 * p_line->i_blue + 128) >> 8) + 128;
740 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
741 18 * p_line->i_blue + 128) >> 8) + 128;
744 fmt.p_palette->i_entries = 16;
745 for( i = 0; i < 8; i++ )
747 fmt.p_palette->palette[i][0] = 0;
748 fmt.p_palette->palette[i][1] = 0x80;
749 fmt.p_palette->palette[i][2] = 0x80;
750 fmt.p_palette->palette[i][3] = pi_gamma[i];
751 fmt.p_palette->palette[i][3] =
752 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
754 for( i = 8; i < fmt.p_palette->i_entries; i++ )
756 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
757 fmt.p_palette->palette[i][1] = i_u;
758 fmt.p_palette->palette[i][2] = i_v;
759 fmt.p_palette->palette[i][3] = pi_gamma[i];
760 fmt.p_palette->palette[i][3] =
761 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
764 p_dst = p_region->p_picture->Y_PIXELS;
765 i_pitch = p_region->p_picture->Y_PITCH;
767 /* Initialize the region pixels */
768 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
770 for( ; p_line != NULL; p_line = p_line->p_next )
772 int i_glyph_tmax = 0;
773 int i_bitmap_offset, i_offset, i_align_offset = 0;
774 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
776 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
777 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
780 if( p_line->i_width < i_width )
782 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
784 i_align_offset = i_width - p_line->i_width;
786 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
788 i_align_offset = ( i_width - p_line->i_width ) / 2;
792 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
794 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
796 i_offset = ( p_line->p_glyph_pos[ i ].y +
797 i_glyph_tmax - p_glyph->top + 2 ) *
798 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
801 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
803 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
805 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
807 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
814 /* Outlining (find something better than nearest neighbour filtering ?) */
817 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
818 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
819 uint8_t left, current;
821 for( y = 1; y < (int)fmt.i_height - 1; y++ )
823 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
824 p_dst += p_region->p_picture->Y_PITCH;
827 for( x = 1; x < (int)fmt.i_width - 1; x++ )
830 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
831 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;
835 memset( p_top, 0, fmt.i_width );
841 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
842 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
843 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
844 int i_glyph_tmax, int i_align_offset,
845 uint8_t i_y, uint8_t i_u, uint8_t i_v,
846 subpicture_region_t *p_region)
850 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
852 p_dst_y = p_region->p_picture->Y_PIXELS;
853 p_dst_u = p_region->p_picture->U_PIXELS;
854 p_dst_v = p_region->p_picture->V_PIXELS;
855 p_dst_a = p_region->p_picture->A_PIXELS;
856 i_pitch = p_region->p_picture->A_PITCH;
858 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
859 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
861 for( y = 0; y < i_line_thickness; y++ )
863 int i_extra = p_this_glyph->bitmap.width;
867 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
868 (p_this_glyph_pos->x + p_this_glyph->left);
870 for( x = 0; x < i_extra; x++ )
874 /* break the underline around the tails of any glyphs which cross it */
875 for( z = x - i_line_thickness;
876 z < x + i_line_thickness && b_ok;
879 if( p_next_glyph && ( z >= i_extra ) )
881 int i_row = i_line_offset + p_next_glyph->top + y;
883 if( ( p_next_glyph->bitmap.rows > i_row ) &&
884 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
889 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
891 int i_row = i_line_offset + p_this_glyph->top + y;
893 if( ( p_this_glyph->bitmap.rows > i_row ) &&
894 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
903 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
904 p_dst_u[i_offset+x] = i_u;
905 p_dst_v[i_offset+x] = i_v;
906 p_dst_a[i_offset+x] = 255;
913 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
915 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
916 int i_pitch = p_region->p_picture->A_PITCH;
919 for( ; p_line != NULL; p_line = p_line->p_next )
921 int i_glyph_tmax=0, i = 0;
922 int i_bitmap_offset, i_offset, i_align_offset = 0;
923 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
925 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
926 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
929 if( p_line->i_width < i_width )
931 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
933 i_align_offset = i_width - p_line->i_width;
935 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
937 i_align_offset = ( i_width - p_line->i_width ) / 2;
941 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
943 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
945 i_offset = ( p_line->p_glyph_pos[ i ].y +
946 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
947 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
948 i_align_offset +xoffset;
950 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
952 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
954 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
955 if( p_dst[i_offset+x] <
956 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
958 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
967 /*****************************************************************************
968 * Render: place string in picture
969 *****************************************************************************
970 * This function merges the previously rendered freetype glyphs into a picture
971 *****************************************************************************/
972 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
973 line_desc_t *p_line, int i_width, int i_height )
975 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
977 int i, x, y, i_pitch, i_alpha;
978 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
980 if( i_width == 0 || i_height == 0 )
983 /* Create a new subpicture region */
984 memset( &fmt, 0, sizeof(video_format_t) );
985 fmt.i_chroma = VLC_CODEC_YUVA;
987 fmt.i_width = fmt.i_visible_width = i_width + 6;
988 fmt.i_height = fmt.i_visible_height = i_height + 6;
989 if( p_region->fmt.i_visible_width > 0 )
990 fmt.i_visible_width = p_region->fmt.i_visible_width;
991 if( p_region->fmt.i_visible_height > 0 )
992 fmt.i_visible_height = p_region->fmt.i_visible_height;
993 fmt.i_x_offset = fmt.i_y_offset = 0;
995 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
996 if( !p_region->p_picture )
1000 /* Calculate text color components */
1001 YUVFromRGB( (p_line->i_red << 16) |
1002 (p_line->i_green << 8) |
1005 i_alpha = p_line->i_alpha;
1007 p_dst_y = p_region->p_picture->Y_PIXELS;
1008 p_dst_u = p_region->p_picture->U_PIXELS;
1009 p_dst_v = p_region->p_picture->V_PIXELS;
1010 p_dst_a = p_region->p_picture->A_PIXELS;
1011 i_pitch = p_region->p_picture->A_PITCH;
1013 /* Initialize the region pixels */
1014 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
1016 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
1017 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1018 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1019 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1023 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1024 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1025 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1026 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1028 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1029 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1031 DrawBlack( p_line, i_width, p_region, 0, 0);
1032 DrawBlack( p_line, i_width, p_region, -1, 0);
1033 DrawBlack( p_line, i_width, p_region, 0, -1);
1034 DrawBlack( p_line, i_width, p_region, 1, 0);
1035 DrawBlack( p_line, i_width, p_region, 0, 1);
1038 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1040 DrawBlack( p_line, i_width, p_region, -1, -1);
1041 DrawBlack( p_line, i_width, p_region, -1, 1);
1042 DrawBlack( p_line, i_width, p_region, 1, -1);
1043 DrawBlack( p_line, i_width, p_region, 1, 1);
1045 DrawBlack( p_line, i_width, p_region, -2, 0);
1046 DrawBlack( p_line, i_width, p_region, 0, -2);
1047 DrawBlack( p_line, i_width, p_region, 2, 0);
1048 DrawBlack( p_line, i_width, p_region, 0, 2);
1050 DrawBlack( p_line, i_width, p_region, -2, -2);
1051 DrawBlack( p_line, i_width, p_region, -2, 2);
1052 DrawBlack( p_line, i_width, p_region, 2, -2);
1053 DrawBlack( p_line, i_width, p_region, 2, 2);
1055 DrawBlack( p_line, i_width, p_region, -3, 0);
1056 DrawBlack( p_line, i_width, p_region, 0, -3);
1057 DrawBlack( p_line, i_width, p_region, 3, 0);
1058 DrawBlack( p_line, i_width, p_region, 0, 3);
1061 for( ; p_line != NULL; p_line = p_line->p_next )
1063 int i_glyph_tmax = 0;
1064 int i_bitmap_offset, i_offset, i_align_offset = 0;
1065 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1067 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1068 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1071 if( p_line->i_width < i_width )
1073 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1075 i_align_offset = i_width - p_line->i_width;
1077 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1079 i_align_offset = ( i_width - p_line->i_width ) / 2;
1083 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1085 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1087 i_offset = ( p_line->p_glyph_pos[ i ].y +
1088 i_glyph_tmax - p_glyph->top + 3 ) *
1089 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1092 if( p_line->b_new_color_mode )
1094 /* Every glyph can (and in fact must) have its own color */
1095 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1098 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1100 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1102 uint8_t i_y_local = i_y;
1103 uint8_t i_u_local = i_u;
1104 uint8_t i_v_local = i_v;
1106 if( p_line->p_fg_bg_ratio != 0x00 )
1108 int i_split = p_glyph->bitmap.width *
1109 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1113 YUVFromRGB( p_line->p_bg_rgb[ i ],
1114 &i_y_local, &i_u_local, &i_v_local );
1118 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1120 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1121 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1123 p_dst_u[i_offset+x] = i_u;
1124 p_dst_v[i_offset+x] = i_v;
1126 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1127 p_dst_a[i_offset+x] = 0xff;
1130 i_offset += i_pitch;
1133 if( p_line->pi_underline_thickness[ i ] )
1135 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1136 p_line->pi_underline_offset[ i ],
1137 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1138 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1139 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1140 i_glyph_tmax, i_align_offset,
1147 /* Apply the alpha setting */
1148 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1149 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1155 * This function renders a text subpicture region into another one.
1156 * It also calculates the size needed for this string, and renders the
1157 * needed glyphs into memory. It is used as pf_add_string callback in
1158 * the vout method by this module
1160 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1161 subpicture_region_t *p_region_in )
1163 filter_sys_t *p_sys = p_filter->p_sys;
1164 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1165 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1166 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1167 int i_string_length;
1169 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1170 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1180 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1181 psz_string = p_region_in->psz_text;
1182 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1184 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1185 i_scale = val.i_int;
1187 if( p_region_in->p_style )
1189 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1190 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1191 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1195 i_font_color = p_sys->i_font_color;
1196 i_font_alpha = 255 - p_sys->i_font_opacity;
1197 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1200 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1201 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1202 SetFontSize( p_filter, i_font_size );
1204 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1205 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1206 i_blue = i_font_color & 0x000000FF;
1208 result.x = result.y = 0;
1209 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1211 psz_unicode = psz_unicode_orig =
1212 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1213 if( psz_unicode == NULL )
1215 #if defined(WORDS_BIGENDIAN)
1216 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1218 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1220 if( iconv_handle == (vlc_iconv_t)-1 )
1222 msg_Warn( p_filter, "unable to do conversion" );
1228 const char *p_in_buffer = psz_string;
1229 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1230 i_in_bytes = strlen( psz_string );
1231 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1232 i_out_bytes_left = i_out_bytes;
1233 p_out_buffer = (char *)psz_unicode;
1234 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1236 &p_out_buffer, &i_out_bytes_left );
1238 vlc_iconv_close( iconv_handle );
1242 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1243 "bytes left %u", (unsigned)i_in_bytes );
1246 *(uint32_t*)p_out_buffer = 0;
1247 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1250 #if defined(HAVE_FRIBIDI)
1252 uint32_t *p_fribidi_string;
1253 int32_t start_pos, pos = 0;
1255 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1256 if( !p_fribidi_string )
1259 /* Do bidi conversion line-by-line */
1260 while( pos < i_string_length )
1262 while( pos < i_string_length )
1264 i_char = psz_unicode[pos];
1265 if (i_char != '\r' && i_char != '\n')
1267 p_fribidi_string[pos] = i_char;
1271 while( pos < i_string_length )
1273 i_char = psz_unicode[pos];
1274 if (i_char == '\r' || i_char == '\n')
1278 if (pos > start_pos)
1280 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1281 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1284 (FriBidiChar*)p_fribidi_string + start_pos,
1289 free( psz_unicode_orig );
1290 psz_unicode = psz_unicode_orig = p_fribidi_string;
1291 p_fribidi_string[ i_string_length ] = 0;
1295 /* Calculate relative glyph positions and a bounding box for the
1297 if( !(p_line = NewLine( strlen( psz_string ))) )
1300 i_pen_x = i_pen_y = 0;
1302 psz_line_start = psz_unicode;
1304 #define face p_sys->p_face
1305 #define glyph face->glyph
1307 while( *psz_unicode )
1309 i_char = *psz_unicode++;
1310 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1315 if( i_char == '\n' )
1317 psz_line_start = psz_unicode;
1318 if( !(p_next = NewLine( strlen( psz_string ))) )
1320 p_line->p_next = p_next;
1321 p_line->i_width = line.xMax;
1322 p_line->i_height = face->size->metrics.height >> 6;
1323 p_line->pp_glyphs[ i ] = NULL;
1324 p_line->i_alpha = i_font_alpha;
1325 p_line->i_red = i_red;
1326 p_line->i_green = i_green;
1327 p_line->i_blue = i_blue;
1330 result.x = __MAX( result.x, line.xMax );
1331 result.y += face->size->metrics.height >> 6;
1334 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1335 i_pen_y += face->size->metrics.height >> 6;
1337 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1342 i_glyph_index = FT_Get_Char_Index( face, i_char );
1343 if( p_sys->i_use_kerning && i_glyph_index
1347 FT_Get_Kerning( face, i_previous, i_glyph_index,
1348 ft_kerning_default, &delta );
1349 i_pen_x += delta.x >> 6;
1352 p_line->p_glyph_pos[ i ].x = i_pen_x;
1353 p_line->p_glyph_pos[ i ].y = i_pen_y;
1354 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1357 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1361 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1364 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1368 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1369 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1372 FT_Done_Glyph( tmp_glyph );
1375 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1378 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1379 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1380 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1382 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1383 p_line->pp_glyphs[ i ] = NULL;
1385 p_line = NewLine( strlen( psz_string ));
1386 if( p_prev ) p_prev->p_next = p_line;
1387 else p_lines = p_line;
1389 uint32_t *psz_unicode_saved = psz_unicode;
1390 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1394 if( psz_unicode == psz_line_start )
1395 { /* try harder to break that line */
1396 psz_unicode = psz_unicode_saved;
1397 while( psz_unicode > psz_line_start &&
1398 *psz_unicode != '_' && *psz_unicode != '/' &&
1399 *psz_unicode != '\\' && *psz_unicode != '.' )
1404 if( psz_unicode == psz_line_start )
1406 msg_Warn( p_filter, "unbreakable string" );
1411 *psz_unicode = '\n';
1413 psz_unicode = psz_line_start;
1416 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1419 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1420 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1422 i_previous = i_glyph_index;
1423 i_pen_x += glyph->advance.x >> 6;
1427 p_line->i_width = line.xMax;
1428 p_line->i_height = face->size->metrics.height >> 6;
1429 p_line->pp_glyphs[ i ] = NULL;
1430 p_line->i_alpha = i_font_alpha;
1431 p_line->i_red = i_red;
1432 p_line->i_green = i_green;
1433 p_line->i_blue = i_blue;
1434 result.x = __MAX( result.x, line.xMax );
1435 result.y += line.yMax - line.yMin;
1440 p_region_out->i_x = p_region_in->i_x;
1441 p_region_out->i_y = p_region_in->i_y;
1443 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1444 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1446 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1448 free( psz_unicode_orig );
1449 FreeLines( p_lines );
1453 free( psz_unicode_orig );
1454 FreeLines( p_lines );
1455 return VLC_EGENERIC;
1458 #ifdef HAVE_FONTCONFIG
1459 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1460 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1461 bool b_italic, bool b_uline )
1463 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1467 p_style->i_font_size = i_font_size;
1468 p_style->i_font_color = i_font_color;
1469 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1470 p_style->b_italic = b_italic;
1471 p_style->b_bold = b_bold;
1472 p_style->b_underline = b_uline;
1474 p_style->psz_fontname = strdup( psz_fontname );
1479 static void DeleteStyle( ft_style_t *p_style )
1483 free( p_style->psz_fontname );
1488 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1495 if(( s1->i_font_size == s2->i_font_size ) &&
1496 ( s1->i_font_color == s2->i_font_color ) &&
1497 ( s1->b_italic == s2->b_italic ) &&
1498 ( s1->b_bold == s2->b_bold ) &&
1499 ( s1->b_underline == s2->b_underline ) &&
1500 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1507 static void IconvText( filter_t *p_filter, const char *psz_string,
1508 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1510 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1512 /* If memory hasn't been allocated for our output string, allocate it here
1513 * - the calling function must now be responsible for freeing it.
1515 if( !*ppsz_unicode )
1516 *ppsz_unicode = (uint32_t *)
1517 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1519 /* We don't need to handle a NULL pointer in *ppsz_unicode
1520 * if we are instead testing for a non NULL value like we are here */
1524 #if defined(WORDS_BIGENDIAN)
1525 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1527 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1529 if( iconv_handle != (vlc_iconv_t)-1 )
1531 char *p_in_buffer, *p_out_buffer;
1532 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1533 i_in_bytes = strlen( psz_string );
1534 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1535 i_out_bytes_left = i_out_bytes;
1536 p_in_buffer = (char *) psz_string;
1537 p_out_buffer = (char *) *ppsz_unicode;
1538 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1539 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1541 vlc_iconv_close( iconv_handle );
1545 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1546 "bytes left %u", (unsigned)i_in_bytes );
1550 *(uint32_t*)p_out_buffer = 0;
1552 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1557 msg_Warn( p_filter, "unable to do conversion" );
1562 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1563 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1566 ft_style_t *p_style = NULL;
1568 char *psz_fontname = NULL;
1569 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1570 uint32_t i_karaoke_bg_color = i_font_color;
1571 int i_font_size = p_sys->i_font_size;
1573 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1574 &i_font_color, &i_karaoke_bg_color ))
1576 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1577 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1582 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1583 bool b_uline, int i_karaoke_bgcolor,
1584 line_desc_t *p_line, uint32_t *psz_unicode,
1585 int *pi_pen_x, int i_pen_y, int *pi_start,
1586 FT_Vector *p_result )
1591 bool b_first_on_line = true;
1594 int i_pen_x_start = *pi_pen_x;
1596 uint32_t *psz_unicode_start = psz_unicode;
1598 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1600 /* Account for part of line already in position */
1601 for( i=0; i<*pi_start; i++ )
1605 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1606 ft_glyph_bbox_pixels, &glyph_size );
1608 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1609 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1610 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1611 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1617 b_first_on_line = false;
1619 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1625 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1626 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1630 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1631 ft_kerning_default, &delta );
1632 *pi_pen_x += delta.x >> 6;
1634 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1635 p_line->p_glyph_pos[ i ].y = i_pen_y;
1637 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1641 "unable to render text FT_Load_Glyph returned %d", i_error );
1642 p_line->pp_glyphs[ i ] = NULL;
1643 return VLC_EGENERIC;
1645 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1649 "unable to render text FT_Get_Glyph returned %d", i_error );
1650 p_line->pp_glyphs[ i ] = NULL;
1651 return VLC_EGENERIC;
1653 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1654 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1657 FT_Done_Glyph( tmp_glyph );
1662 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1663 p_face->size->metrics.y_scale));
1664 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1665 p_face->size->metrics.y_scale));
1667 p_line->pi_underline_offset[ i ] =
1668 ( aOffset < 0 ) ? -aOffset : aOffset;
1669 p_line->pi_underline_thickness[ i ] =
1670 ( aSize < 0 ) ? -aSize : aSize;
1672 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1673 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1674 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1675 p_line->p_fg_bg_ratio[ i ] = 0x00;
1677 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1678 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1679 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1681 for( ; i >= *pi_start; i-- )
1682 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1685 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1689 if( psz_unicode == psz_unicode_start )
1691 if( b_first_on_line )
1693 msg_Warn( p_filter, "unbreakable string" );
1694 p_line->pp_glyphs[ i ] = NULL;
1695 return VLC_EGENERIC;
1697 *pi_pen_x = i_pen_x_start;
1699 p_line->i_width = line.xMax;
1700 p_line->i_height = __MAX( p_line->i_height,
1701 p_face->size->metrics.height >> 6 );
1702 p_line->pp_glyphs[ i ] = NULL;
1704 p_result->x = __MAX( p_result->x, line.xMax );
1705 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1706 i_yMax - i_yMin ) );
1711 *psz_unicode = '\n';
1713 psz_unicode = psz_unicode_start;
1714 *pi_pen_x = i_pen_x_start;
1722 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1723 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1725 i_previous = i_glyph_index;
1726 *pi_pen_x += p_face->glyph->advance.x >> 6;
1729 p_line->i_width = line.xMax;
1730 p_line->i_height = __MAX( p_line->i_height,
1731 p_face->size->metrics.height >> 6 );
1732 p_line->pp_glyphs[ i ] = NULL;
1734 p_result->x = __MAX( p_result->x, line.xMax );
1735 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1736 line.yMax - line.yMin ) );
1740 /* Get rid of any text processed - if necessary repositioning
1741 * at the start of a new line of text
1745 *psz_unicode_start = '\0';
1747 else if( psz_unicode > psz_unicode_start )
1749 for( i=0; psz_unicode[ i ]; i++ )
1750 psz_unicode_start[ i ] = psz_unicode[ i ];
1751 psz_unicode_start[ i ] = '\0';
1757 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1758 uint32_t **psz_text_out, uint32_t *pi_runs,
1759 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1760 ft_style_t *p_style )
1762 uint32_t i_string_length = 0;
1764 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1765 *psz_text_out += i_string_length;
1767 if( ppp_styles && ppi_run_lengths )
1773 *ppp_styles = (ft_style_t **)
1774 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1776 else if( *pi_runs == 1 )
1778 *ppp_styles = (ft_style_t **)
1779 malloc( *pi_runs * sizeof( ft_style_t * ) );
1782 /* We have just malloc'ed this memory successfully -
1783 * *pi_runs HAS to be within the memory area of *ppp_styles */
1786 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1790 if( *ppi_run_lengths )
1792 *ppi_run_lengths = (uint32_t *)
1793 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1795 else if( *pi_runs == 1 )
1797 *ppi_run_lengths = (uint32_t *)
1798 malloc( *pi_runs * sizeof( uint32_t ) );
1801 /* same remarks here */
1802 if( *ppi_run_lengths )
1804 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1807 /* If we couldn't use the p_style argument due to memory allocation
1808 * problems above, release it here.
1810 if( p_style ) DeleteStyle( p_style );
1813 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1817 for( k=0; k < p_sys->i_font_attachments; k++ )
1819 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1821 FT_Face p_face = NULL;
1823 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1831 bool match = !strcasecmp( p_face->family_name,
1832 p_style->psz_fontname );
1834 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1835 match = match && p_style->b_bold;
1837 match = match && !p_style->b_bold;
1839 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1840 match = match && p_style->b_italic;
1842 match = match && !p_style->b_italic;
1850 FT_Done_Face( p_face );
1855 return VLC_EGENERIC;
1858 static int ProcessLines( filter_t *p_filter,
1863 uint32_t *pi_run_lengths,
1864 ft_style_t **pp_styles,
1865 line_desc_t **pp_lines,
1867 FT_Vector *p_result,
1871 uint32_t *pi_k_run_lengths,
1872 uint32_t *pi_k_durations )
1874 filter_sys_t *p_sys = p_filter->p_sys;
1875 ft_style_t **pp_char_styles;
1876 int *p_new_positions = NULL;
1877 int8_t *p_levels = NULL;
1878 uint8_t *pi_karaoke_bar = NULL;
1882 /* Assign each character in the text string its style explicitly, so that
1883 * after the characters have been shuffled around by Fribidi, we can re-apply
1884 * the styles, and to simplify the calculation of runs within a line.
1886 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1887 if( !pp_char_styles )
1892 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1893 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1894 * we just won't be able to display the progress bar; at least we'll
1900 for( j = 0; j < i_runs; j++ )
1901 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1902 pp_char_styles[ i++ ] = pp_styles[ j ];
1904 #if defined(HAVE_FRIBIDI)
1906 ft_style_t **pp_char_styles_new;
1907 int *p_old_positions;
1908 uint32_t *p_fribidi_string;
1909 int start_pos, pos = 0;
1911 pp_char_styles_new = (ft_style_t **)
1912 malloc( i_len * sizeof( ft_style_t * ));
1914 p_fribidi_string = (uint32_t *)
1915 malloc( (i_len + 1) * sizeof(uint32_t) );
1916 p_old_positions = (int *)
1917 malloc( (i_len + 1) * sizeof( int ) );
1918 p_new_positions = (int *)
1919 malloc( (i_len + 1) * sizeof( int ) );
1920 p_levels = (int8_t *)
1921 malloc( (i_len + 1) * sizeof( int8_t ) );
1923 if( ! pp_char_styles_new ||
1924 ! p_fribidi_string ||
1925 ! p_old_positions ||
1926 ! p_new_positions ||
1930 free( p_old_positions );
1931 free( p_new_positions );
1932 free( p_fribidi_string );
1933 free( pp_char_styles_new );
1934 free( pi_karaoke_bar );
1936 free( pp_char_styles );
1940 /* Do bidi conversion line-by-line */
1943 while(pos < i_len) {
1944 if (psz_text[pos] != '\n')
1946 p_fribidi_string[pos] = psz_text[pos];
1947 pp_char_styles_new[pos] = pp_char_styles[pos];
1948 p_new_positions[pos] = pos;
1953 while(pos < i_len) {
1954 if (psz_text[pos] == '\n')
1958 if (pos > start_pos)
1960 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1961 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1962 pos - start_pos, &base_dir,
1963 (FriBidiChar*)p_fribidi_string + start_pos,
1964 p_new_positions + start_pos,
1966 p_levels + start_pos );
1967 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1969 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1970 p_old_positions[ j - start_pos ] ];
1971 p_new_positions[ j ] += start_pos;
1975 free( p_old_positions );
1976 free( pp_char_styles );
1977 pp_char_styles = pp_char_styles_new;
1978 psz_text = p_fribidi_string;
1979 p_fribidi_string[ i_len ] = 0;
1982 /* Work out the karaoke */
1983 if( pi_karaoke_bar )
1985 int64_t i_last_duration = 0;
1986 int64_t i_duration = 0;
1987 int64_t i_start_pos = 0;
1988 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1990 for( k = 0; k< i_k_runs; k++ )
1992 double fraction = 0.0;
1994 i_duration += pi_k_durations[ k ];
1996 if( i_duration < i_elapsed )
1998 /* Completely finished this run-length -
1999 * let it render normally */
2003 else if( i_elapsed < i_last_duration )
2005 /* Haven't got up to this segment yet -
2006 * render it completely in karaoke BG mode */
2012 /* Partway through this run */
2014 fraction = (double)(i_elapsed - i_last_duration) /
2015 (double)pi_k_durations[ k ];
2017 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2019 double shade = pi_k_run_lengths[ k ] * fraction;
2021 if( p_new_positions )
2022 j = p_new_positions[ i_start_pos + i ];
2024 j = i_start_pos + i;
2026 if( i < (uint32_t)shade )
2027 pi_karaoke_bar[ j ] = 0xff;
2028 else if( (double)i > shade )
2029 pi_karaoke_bar[ j ] = 0x00;
2032 shade -= (int)shade;
2033 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2034 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2038 i_last_duration = i_duration;
2039 i_start_pos += pi_k_run_lengths[ k ];
2043 free( p_new_positions );
2045 FT_Vector tmp_result;
2047 line_desc_t *p_line = NULL;
2048 line_desc_t *p_prev = NULL;
2054 p_result->x = p_result->y = 0;
2055 tmp_result.x = tmp_result.y = 0;
2058 for( k = 0; k <= (uint32_t) i_len; k++ )
2060 if( ( k == (uint32_t) i_len ) ||
2062 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2064 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2066 /* End of the current style run */
2067 FT_Face p_face = NULL;
2070 /* Look for a match amongst our attachments first */
2071 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2075 char *psz_fontfile = NULL;
2077 vlc_mutex_lock( &fb_lock );
2078 if( p_sys->b_fontconfig_ok )
2080 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2081 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2082 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2083 p_style->psz_fontname,
2088 vlc_mutex_unlock( &fb_lock );
2090 if( psz_fontfile && ! *psz_fontfile )
2092 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2093 " so using default font", p_style->psz_fontname,
2094 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2095 (p_style->b_bold ? "(Bold)" :
2096 (p_style->b_italic ? "(Italic)" : ""))) );
2097 free( psz_fontfile );
2098 psz_fontfile = NULL;
2103 if( FT_New_Face( p_sys->p_library,
2104 psz_fontfile, i_idx, &p_face ) )
2106 free( psz_fontfile );
2107 free( pp_char_styles );
2108 #if defined(HAVE_FRIBIDI)
2111 free( pi_karaoke_bar );
2112 return VLC_EGENERIC;
2114 free( psz_fontfile );
2118 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2120 /* We've loaded a font face which is unhelpful for actually
2121 * rendering text - fallback to the default one.
2123 FT_Done_Face( p_face );
2127 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2128 ft_encoding_unicode ) ||
2129 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2130 p_style->i_font_size ) )
2132 if( p_face ) FT_Done_Face( p_face );
2133 free( pp_char_styles );
2134 #if defined(HAVE_FRIBIDI)
2137 free( pi_karaoke_bar );
2138 return VLC_EGENERIC;
2140 p_sys->i_use_kerning =
2141 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2144 uint32_t *psz_unicode = (uint32_t *)
2145 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2148 if( p_face ) FT_Done_Face( p_face );
2149 free( pp_char_styles );
2150 free( psz_unicode );
2151 #if defined(HAVE_FRIBIDI)
2154 free( pi_karaoke_bar );
2157 memcpy( psz_unicode, psz_text + i_prev,
2158 sizeof( uint32_t ) * ( k - i_prev ) );
2159 psz_unicode[ k - i_prev ] = 0;
2160 while( *psz_unicode )
2164 if( !(p_line = NewLine( i_len - i_prev)) )
2166 if( p_face ) FT_Done_Face( p_face );
2167 free( pp_char_styles );
2168 free( psz_unicode );
2169 #if defined(HAVE_FRIBIDI)
2172 free( pi_karaoke_bar );
2175 /* New Color mode only works in YUVA rendering mode --
2176 * (RGB mode has palette constraints on it). We therefore
2177 * need to populate the legacy colour fields also.
2179 p_line->b_new_color_mode = true;
2180 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2181 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2182 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2183 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2184 p_line->p_next = NULL;
2186 i_pen_y += tmp_result.y;
2190 if( p_prev ) p_prev->p_next = p_line;
2191 else *pp_lines = p_line;
2194 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2195 p_style->i_font_color, p_style->b_underline,
2196 p_style->i_karaoke_bg_color,
2197 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2198 &tmp_result ) != VLC_SUCCESS )
2200 if( p_face ) FT_Done_Face( p_face );
2201 free( pp_char_styles );
2202 free( psz_unicode );
2203 #if defined(HAVE_FRIBIDI)
2206 free( pi_karaoke_bar );
2207 return VLC_EGENERIC;
2212 p_result->x = __MAX( p_result->x, tmp_result.x );
2213 p_result->y += tmp_result.y;
2218 if( *psz_unicode == '\n')
2222 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2224 *c_ptr = *(c_ptr+1);
2229 free( psz_unicode );
2230 if( p_face ) FT_Done_Face( p_face );
2234 free( pp_char_styles );
2235 #if defined(HAVE_FRIBIDI)
2240 p_result->x = __MAX( p_result->x, tmp_result.x );
2241 p_result->y += tmp_result.y;
2244 if( pi_karaoke_bar )
2247 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2249 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2251 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2255 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2257 /* 100% BG colour will render faster if we
2258 * instead make it 100% FG colour, so leave
2259 * the ratio alone and copy the value across
2261 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2265 if( pi_karaoke_bar[ i ] & 0x80 )
2267 /* Swap Left and Right sides over for Right aligned
2268 * language text (eg. Arabic, Hebrew)
2270 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2272 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2273 p_line->p_bg_rgb[ k ] = i_tmp;
2275 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2278 /* Jump over the '\n' at the line-end */
2281 free( pi_karaoke_bar );
2287 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2288 subpicture_region_t *p_region_in )
2290 int rv = VLC_SUCCESS;
2291 stream_t *p_sub = NULL;
2292 xml_reader_t *p_xml_reader = NULL;
2294 if( !p_region_in || !p_region_in->psz_html )
2295 return VLC_EGENERIC;
2297 /* Reset the default fontsize in case screen metrics have changed */
2298 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2300 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2301 (uint8_t *) p_region_in->psz_html,
2302 strlen( p_region_in->psz_html ),
2306 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2307 if( p_filter->p_sys->p_xml )
2309 bool b_karaoke = false;
2311 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2314 /* Look for Root Node */
2315 if( xml_ReaderRead( p_xml_reader ) == 1 )
2317 char *psz_node = xml_ReaderName( p_xml_reader );
2319 if( !strcasecmp( "karaoke", psz_node ) )
2321 /* We're going to have to render the text a number
2322 * of times to show the progress marker on the text.
2324 var_SetBool( p_filter, "text-rerender", true );
2327 else if( !strcasecmp( "text", psz_node ) )
2333 /* Only text and karaoke tags are supported */
2334 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2335 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2336 p_xml_reader = NULL;
2348 uint32_t i_runs = 0;
2349 uint32_t i_k_runs = 0;
2350 uint32_t *pi_run_lengths = NULL;
2351 uint32_t *pi_k_run_lengths = NULL;
2352 uint32_t *pi_k_durations = NULL;
2353 ft_style_t **pp_styles = NULL;
2355 line_desc_t *p_lines = NULL;
2357 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2358 sizeof( uint32_t ) );
2363 rv = ProcessNodes( p_filter, p_xml_reader,
2364 p_region_in->p_style, psz_text, &i_len,
2365 &i_runs, &pi_run_lengths, &pp_styles,
2367 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2370 p_region_out->i_x = p_region_in->i_x;
2371 p_region_out->i_y = p_region_in->i_y;
2373 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2375 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2376 pi_run_lengths, pp_styles, &p_lines, &result,
2377 b_karaoke, i_k_runs, pi_k_run_lengths,
2381 for( k=0; k<i_runs; k++)
2382 DeleteStyle( pp_styles[k] );
2384 free( pi_run_lengths );
2387 /* Don't attempt to render text that couldn't be layed out
2390 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2392 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2394 Render( p_filter, p_region_out, p_lines,
2395 result.x, result.y );
2399 RenderYUVA( p_filter, p_region_out, p_lines,
2400 result.x, result.y );
2404 FreeLines( p_lines );
2406 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2409 stream_Delete( p_sub );
2415 static char* FontConfig_Select( FcConfig* priv, const char* family,
2416 bool b_bold, bool b_italic, int *i_idx )
2419 FcPattern *pat, *p_pat;
2423 pat = FcPatternCreate();
2424 if (!pat) return NULL;
2426 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2427 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2428 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2429 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2431 FcDefaultSubstitute( pat );
2433 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2435 FcPatternDestroy( pat );
2439 p_pat = FcFontMatch( priv, pat, &result );
2440 FcPatternDestroy( pat );
2441 if( !p_pat ) return NULL;
2443 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2444 || ( val_b != FcTrue ) )
2446 FcPatternDestroy( p_pat );
2449 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2454 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2456 FcPatternDestroy( p_pat );
2461 if( strcasecmp((const char*)val_s, family ) != 0 )
2462 msg_Warn( p_filter, "fontconfig: selected font family is not"
2463 "the requested one: '%s' != '%s'\n",
2464 (const char*)val_s, family );
2467 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2469 FcPatternDestroy( p_pat );
2473 FcPatternDestroy( p_pat );
2474 return strdup( (const char*)val_s );
2478 static void FreeLine( line_desc_t *p_line )
2481 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2483 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2485 free( p_line->pp_glyphs );
2486 free( p_line->p_glyph_pos );
2487 free( p_line->p_fg_rgb );
2488 free( p_line->p_bg_rgb );
2489 free( p_line->p_fg_bg_ratio );
2490 free( p_line->pi_underline_offset );
2491 free( p_line->pi_underline_thickness );
2495 static void FreeLines( line_desc_t *p_lines )
2497 line_desc_t *p_line, *p_next;
2499 if( !p_lines ) return;
2501 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2503 p_next = p_line->p_next;
2508 static line_desc_t *NewLine( int i_count )
2510 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2512 if( !p_line ) return NULL;
2513 p_line->i_height = 0;
2514 p_line->i_width = 0;
2515 p_line->p_next = NULL;
2517 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2518 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2519 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2520 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2521 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2522 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2523 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2524 if( ( p_line->pp_glyphs == NULL ) ||
2525 ( p_line->p_glyph_pos == NULL ) ||
2526 ( p_line->p_fg_rgb == NULL ) ||
2527 ( p_line->p_bg_rgb == NULL ) ||
2528 ( p_line->p_fg_bg_ratio == NULL ) ||
2529 ( p_line->pi_underline_offset == NULL ) ||
2530 ( p_line->pi_underline_thickness == NULL ) )
2532 free( p_line->pi_underline_thickness );
2533 free( p_line->pi_underline_offset );
2534 free( p_line->p_fg_rgb );
2535 free( p_line->p_bg_rgb );
2536 free( p_line->p_fg_bg_ratio );
2537 free( p_line->p_glyph_pos );
2538 free( p_line->pp_glyphs );
2542 p_line->pp_glyphs[0] = NULL;
2543 p_line->b_new_color_mode = false;
2548 static int GetFontSize( filter_t *p_filter )
2550 filter_sys_t *p_sys = p_filter->p_sys;
2554 if( p_sys->i_default_font_size )
2556 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2557 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2559 i_size = p_sys->i_default_font_size;
2563 var_Get( p_filter, "freetype-rel-fontsize", &val );
2566 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2567 p_filter->p_sys->i_display_height =
2568 p_filter->fmt_out.video.i_height;
2573 msg_Warn( p_filter, "invalid fontsize, using 12" );
2574 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2575 i_size = 12 * val.i_int / 1000;
2582 static int SetFontSize( filter_t *p_filter, int i_size )
2584 filter_sys_t *p_sys = p_filter->p_sys;
2588 i_size = GetFontSize( p_filter );
2590 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2593 p_sys->i_font_size = i_size;
2595 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2597 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2598 return VLC_EGENERIC;
2604 static void YUVFromRGB( uint32_t i_argb,
2605 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2607 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2608 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2609 int i_blue = ( i_argb & 0x000000ff );
2611 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2612 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2613 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2614 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2615 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2616 -585 * i_blue + 4096 + 1048576) >> 13, 240);