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 if( p_sys->p_xml ) 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 msg_Warn( p_filter, "Building the Fontconfig cache" );
546 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
548 vlc_mutex_unlock( &fb_lock );
549 return p_fontbuilder;
551 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
553 vlc_mutex_lock( &fb_lock );
556 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
558 /* We wait for the thread on the first FontBuilderDetach */
559 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
561 var_SetBool( p_fontbuilder, "build-joined", true );
562 vlc_mutex_unlock( &fb_lock );
563 /* We need to unlock otherwise we may not join (the thread waiting
564 * for the lock). It is safe to unlock as no one else will try a
565 * join and we have a reference on the object) */
566 vlc_thread_join( p_fontbuilder );
567 vlc_mutex_lock( &fb_lock );
569 vlc_object_release( p_fontbuilder );
571 vlc_mutex_unlock( &fb_lock );
573 static void* FontBuilderThread( vlc_object_t *p_this )
575 FcConfig *p_fontconfig = FcInitLoadConfig();
580 int canc = vlc_savecancel ();
582 //msg_Dbg( p_this, "Building font database..." );
583 msg_Dbg( p_this, "Building font database..." );
585 if(! FcConfigBuildFonts( p_fontconfig ))
587 /* Don't destroy the fontconfig object - we won't be able to do
588 * italics or bold or change the font face, but we will still
589 * be able to do underline and change the font size.
591 msg_Err( p_this, "fontconfig database can't be built. "
592 "Font styling won't be available" );
596 msg_Dbg( p_this, "Finished building font database." );
597 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
599 vlc_mutex_lock( &fb_lock );
600 p_this->p_private = p_fontconfig;
601 vlc_mutex_unlock( &fb_lock );
603 var_SetBool( p_this, "build-done", true );
604 vlc_restorecancel (canc);
608 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
610 filter_sys_t *p_sys = p_filter->p_sys;
612 p_sys->p_fontconfig = p_fontbuilder->p_private;
613 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
615 static void FontBuilderDestructor( vlc_object_t *p_this )
617 FcConfig *p_fontconfig = p_this->p_private;
620 FcConfigDestroy( p_fontconfig );
622 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
623 vlc_value_t oldval, vlc_value_t newval, void *param )
625 filter_t *p_filter = param;
629 vlc_mutex_lock( &fb_lock );
631 FontBuilderGetFcConfig( p_filter, p_this );
633 vlc_mutex_unlock( &fb_lock );
642 /*****************************************************************************
643 * Make any TTF/OTF fonts present in the attachments of the media file
644 * and store them for later use by the FreeType Engine
645 *****************************************************************************/
646 static int LoadFontsFromAttachments( filter_t *p_filter )
648 filter_sys_t *p_sys = p_filter->p_sys;
649 input_thread_t *p_input;
650 input_attachment_t **pp_attachments;
651 int i_attachments_cnt;
653 int rv = VLC_SUCCESS;
655 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
659 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
661 vlc_object_release(p_input);
665 p_sys->i_font_attachments = 0;
666 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
667 if(! p_sys->pp_font_attachments )
670 for( k = 0; k < i_attachments_cnt; k++ )
672 input_attachment_t *p_attach = pp_attachments[k];
674 if( p_sys->pp_font_attachments )
676 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
677 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
678 ( p_attach->i_data > 0 ) &&
679 ( p_attach->p_data != NULL ) )
681 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
685 vlc_input_attachment_Delete( p_attach );
690 vlc_input_attachment_Delete( p_attach );
693 free( pp_attachments );
695 vlc_object_release(p_input);
700 /*****************************************************************************
701 * Render: place string in picture
702 *****************************************************************************
703 * This function merges the previously rendered freetype glyphs into a picture
704 *****************************************************************************/
705 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
706 line_desc_t *p_line, int i_width, int i_height )
708 static const uint8_t pi_gamma[16] =
709 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
710 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
714 int i, x, y, i_pitch;
715 uint8_t i_y; /* YUV values, derived from incoming RGB */
718 /* Create a new subpicture region */
719 memset( &fmt, 0, sizeof(video_format_t) );
720 fmt.i_chroma = VLC_CODEC_YUVP;
722 fmt.i_width = fmt.i_visible_width = i_width + 4;
723 fmt.i_height = fmt.i_visible_height = i_height + 4;
724 if( p_region->fmt.i_visible_width > 0 )
725 fmt.i_visible_width = p_region->fmt.i_visible_width;
726 if( p_region->fmt.i_visible_height > 0 )
727 fmt.i_visible_height = p_region->fmt.i_visible_height;
728 fmt.i_x_offset = fmt.i_y_offset = 0;
730 assert( !p_region->p_picture );
731 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
732 if( !p_region->p_picture )
736 /* Calculate text color components */
737 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
738 25 * p_line->i_blue + 128) >> 8) + 16;
739 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
740 112 * p_line->i_blue + 128) >> 8) + 128;
741 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
742 18 * p_line->i_blue + 128) >> 8) + 128;
745 fmt.p_palette->i_entries = 16;
746 for( i = 0; i < 8; i++ )
748 fmt.p_palette->palette[i][0] = 0;
749 fmt.p_palette->palette[i][1] = 0x80;
750 fmt.p_palette->palette[i][2] = 0x80;
751 fmt.p_palette->palette[i][3] = pi_gamma[i];
752 fmt.p_palette->palette[i][3] =
753 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
755 for( i = 8; i < fmt.p_palette->i_entries; i++ )
757 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
758 fmt.p_palette->palette[i][1] = i_u;
759 fmt.p_palette->palette[i][2] = i_v;
760 fmt.p_palette->palette[i][3] = pi_gamma[i];
761 fmt.p_palette->palette[i][3] =
762 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
765 p_dst = p_region->p_picture->Y_PIXELS;
766 i_pitch = p_region->p_picture->Y_PITCH;
768 /* Initialize the region pixels */
769 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
771 for( ; p_line != NULL; p_line = p_line->p_next )
773 int i_glyph_tmax = 0;
774 int i_bitmap_offset, i_offset, i_align_offset = 0;
775 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
777 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
778 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
781 if( p_line->i_width < i_width )
783 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
785 i_align_offset = i_width - p_line->i_width;
787 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
789 i_align_offset = ( i_width - p_line->i_width ) / 2;
793 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
795 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
797 i_offset = ( p_line->p_glyph_pos[ i ].y +
798 i_glyph_tmax - p_glyph->top + 2 ) *
799 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
802 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
804 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
806 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
808 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
815 /* Outlining (find something better than nearest neighbour filtering ?) */
818 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
819 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
820 uint8_t left, current;
822 for( y = 1; y < (int)fmt.i_height - 1; y++ )
824 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
825 p_dst += p_region->p_picture->Y_PITCH;
828 for( x = 1; x < (int)fmt.i_width - 1; x++ )
831 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
832 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;
836 memset( p_top, 0, fmt.i_width );
842 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
843 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
844 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
845 int i_glyph_tmax, int i_align_offset,
846 uint8_t i_y, uint8_t i_u, uint8_t i_v,
847 subpicture_region_t *p_region)
851 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
853 p_dst_y = p_region->p_picture->Y_PIXELS;
854 p_dst_u = p_region->p_picture->U_PIXELS;
855 p_dst_v = p_region->p_picture->V_PIXELS;
856 p_dst_a = p_region->p_picture->A_PIXELS;
857 i_pitch = p_region->p_picture->A_PITCH;
859 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
860 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
862 for( y = 0; y < i_line_thickness; y++ )
864 int i_extra = p_this_glyph->bitmap.width;
868 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
869 (p_this_glyph_pos->x + p_this_glyph->left);
871 for( x = 0; x < i_extra; x++ )
875 /* break the underline around the tails of any glyphs which cross it */
876 for( z = x - i_line_thickness;
877 z < x + i_line_thickness && b_ok;
880 if( p_next_glyph && ( z >= i_extra ) )
882 int i_row = i_line_offset + p_next_glyph->top + y;
884 if( ( p_next_glyph->bitmap.rows > i_row ) &&
885 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
890 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
892 int i_row = i_line_offset + p_this_glyph->top + y;
894 if( ( p_this_glyph->bitmap.rows > i_row ) &&
895 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
904 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
905 p_dst_u[i_offset+x] = i_u;
906 p_dst_v[i_offset+x] = i_v;
907 p_dst_a[i_offset+x] = 255;
914 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
916 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
917 int i_pitch = p_region->p_picture->A_PITCH;
920 for( ; p_line != NULL; p_line = p_line->p_next )
922 int i_glyph_tmax=0, i = 0;
923 int i_bitmap_offset, i_offset, i_align_offset = 0;
924 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
926 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
927 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
930 if( p_line->i_width < i_width )
932 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
934 i_align_offset = i_width - p_line->i_width;
936 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
938 i_align_offset = ( i_width - p_line->i_width ) / 2;
942 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
944 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
946 i_offset = ( p_line->p_glyph_pos[ i ].y +
947 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
948 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
949 i_align_offset +xoffset;
951 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
953 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
955 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
956 if( p_dst[i_offset+x] <
957 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
959 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
968 /*****************************************************************************
969 * Render: place string in picture
970 *****************************************************************************
971 * This function merges the previously rendered freetype glyphs into a picture
972 *****************************************************************************/
973 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
974 line_desc_t *p_line, int i_width, int i_height )
976 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
978 int i, x, y, i_pitch, i_alpha;
979 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
981 if( i_width == 0 || i_height == 0 )
984 /* Create a new subpicture region */
985 memset( &fmt, 0, sizeof(video_format_t) );
986 fmt.i_chroma = VLC_CODEC_YUVA;
988 fmt.i_width = fmt.i_visible_width = i_width + 6;
989 fmt.i_height = fmt.i_visible_height = i_height + 6;
990 if( p_region->fmt.i_visible_width > 0 )
991 fmt.i_visible_width = p_region->fmt.i_visible_width;
992 if( p_region->fmt.i_visible_height > 0 )
993 fmt.i_visible_height = p_region->fmt.i_visible_height;
994 fmt.i_x_offset = fmt.i_y_offset = 0;
996 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
997 if( !p_region->p_picture )
1001 /* Calculate text color components */
1002 YUVFromRGB( (p_line->i_red << 16) |
1003 (p_line->i_green << 8) |
1006 i_alpha = p_line->i_alpha;
1008 p_dst_y = p_region->p_picture->Y_PIXELS;
1009 p_dst_u = p_region->p_picture->U_PIXELS;
1010 p_dst_v = p_region->p_picture->V_PIXELS;
1011 p_dst_a = p_region->p_picture->A_PIXELS;
1012 i_pitch = p_region->p_picture->A_PITCH;
1014 /* Initialize the region pixels */
1015 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
1017 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
1018 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1019 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1020 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1024 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1025 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1026 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1027 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1029 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1030 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1032 DrawBlack( p_line, i_width, p_region, 0, 0);
1033 DrawBlack( p_line, i_width, p_region, -1, 0);
1034 DrawBlack( p_line, i_width, p_region, 0, -1);
1035 DrawBlack( p_line, i_width, p_region, 1, 0);
1036 DrawBlack( p_line, i_width, p_region, 0, 1);
1039 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
1044 DrawBlack( p_line, i_width, p_region, 1, 1);
1046 DrawBlack( p_line, i_width, p_region, -2, 0);
1047 DrawBlack( p_line, i_width, p_region, 0, -2);
1048 DrawBlack( p_line, i_width, p_region, 2, 0);
1049 DrawBlack( p_line, i_width, p_region, 0, 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);
1054 DrawBlack( p_line, i_width, p_region, 2, 2);
1056 DrawBlack( p_line, i_width, p_region, -3, 0);
1057 DrawBlack( p_line, i_width, p_region, 0, -3);
1058 DrawBlack( p_line, i_width, p_region, 3, 0);
1059 DrawBlack( p_line, i_width, p_region, 0, 3);
1062 for( ; p_line != NULL; p_line = p_line->p_next )
1064 int i_glyph_tmax = 0;
1065 int i_bitmap_offset, i_offset, i_align_offset = 0;
1066 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1068 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1069 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1072 if( p_line->i_width < i_width )
1074 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1076 i_align_offset = i_width - p_line->i_width;
1078 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1080 i_align_offset = ( i_width - p_line->i_width ) / 2;
1084 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1086 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1088 i_offset = ( p_line->p_glyph_pos[ i ].y +
1089 i_glyph_tmax - p_glyph->top + 3 ) *
1090 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1093 if( p_line->b_new_color_mode )
1095 /* Every glyph can (and in fact must) have its own color */
1096 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1099 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1101 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1103 uint8_t i_y_local = i_y;
1104 uint8_t i_u_local = i_u;
1105 uint8_t i_v_local = i_v;
1107 if( p_line->p_fg_bg_ratio != 0x00 )
1109 int i_split = p_glyph->bitmap.width *
1110 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1114 YUVFromRGB( p_line->p_bg_rgb[ i ],
1115 &i_y_local, &i_u_local, &i_v_local );
1119 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1121 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1122 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1124 p_dst_u[i_offset+x] = i_u;
1125 p_dst_v[i_offset+x] = i_v;
1127 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1128 p_dst_a[i_offset+x] = 0xff;
1131 i_offset += i_pitch;
1134 if( p_line->pi_underline_thickness[ i ] )
1136 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1137 p_line->pi_underline_offset[ i ],
1138 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1139 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1140 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1141 i_glyph_tmax, i_align_offset,
1148 /* Apply the alpha setting */
1149 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1150 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1156 * This function renders a text subpicture region into another one.
1157 * It also calculates the size needed for this string, and renders the
1158 * needed glyphs into memory. It is used as pf_add_string callback in
1159 * the vout method by this module
1161 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1162 subpicture_region_t *p_region_in )
1164 filter_sys_t *p_sys = p_filter->p_sys;
1165 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1166 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1167 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1168 int i_string_length;
1170 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1171 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1181 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1182 psz_string = p_region_in->psz_text;
1183 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1185 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1186 i_scale = val.i_int;
1188 if( p_region_in->p_style )
1190 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1191 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1192 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1196 i_font_color = p_sys->i_font_color;
1197 i_font_alpha = 255 - p_sys->i_font_opacity;
1198 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1201 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1202 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1203 SetFontSize( p_filter, i_font_size );
1205 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1206 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1207 i_blue = i_font_color & 0x000000FF;
1209 result.x = result.y = 0;
1210 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1212 psz_unicode = psz_unicode_orig =
1213 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1214 if( psz_unicode == NULL )
1216 #if defined(WORDS_BIGENDIAN)
1217 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1219 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1221 if( iconv_handle == (vlc_iconv_t)-1 )
1223 msg_Warn( p_filter, "unable to do conversion" );
1229 const char *p_in_buffer = psz_string;
1230 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1231 i_in_bytes = strlen( psz_string );
1232 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1233 i_out_bytes_left = i_out_bytes;
1234 p_out_buffer = (char *)psz_unicode;
1235 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1237 &p_out_buffer, &i_out_bytes_left );
1239 vlc_iconv_close( iconv_handle );
1243 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1244 "bytes left %u", (unsigned)i_in_bytes );
1247 *(uint32_t*)p_out_buffer = 0;
1248 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1251 #if defined(HAVE_FRIBIDI)
1253 uint32_t *p_fribidi_string;
1254 int32_t start_pos, pos = 0;
1256 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1257 if( !p_fribidi_string )
1260 /* Do bidi conversion line-by-line */
1261 while( pos < i_string_length )
1263 while( pos < i_string_length )
1265 i_char = psz_unicode[pos];
1266 if (i_char != '\r' && i_char != '\n')
1268 p_fribidi_string[pos] = i_char;
1272 while( pos < i_string_length )
1274 i_char = psz_unicode[pos];
1275 if (i_char == '\r' || i_char == '\n')
1279 if (pos > start_pos)
1281 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1282 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1285 (FriBidiChar*)p_fribidi_string + start_pos,
1290 free( psz_unicode_orig );
1291 psz_unicode = psz_unicode_orig = p_fribidi_string;
1292 p_fribidi_string[ i_string_length ] = 0;
1296 /* Calculate relative glyph positions and a bounding box for the
1298 if( !(p_line = NewLine( strlen( psz_string ))) )
1301 i_pen_x = i_pen_y = 0;
1303 psz_line_start = psz_unicode;
1305 #define face p_sys->p_face
1306 #define glyph face->glyph
1308 while( *psz_unicode )
1310 i_char = *psz_unicode++;
1311 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1316 if( i_char == '\n' )
1318 psz_line_start = psz_unicode;
1319 if( !(p_next = NewLine( strlen( psz_string ))) )
1321 p_line->p_next = p_next;
1322 p_line->i_width = line.xMax;
1323 p_line->i_height = face->size->metrics.height >> 6;
1324 p_line->pp_glyphs[ i ] = NULL;
1325 p_line->i_alpha = i_font_alpha;
1326 p_line->i_red = i_red;
1327 p_line->i_green = i_green;
1328 p_line->i_blue = i_blue;
1331 result.x = __MAX( result.x, line.xMax );
1332 result.y += face->size->metrics.height >> 6;
1335 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1336 i_pen_y += face->size->metrics.height >> 6;
1338 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1343 i_glyph_index = FT_Get_Char_Index( face, i_char );
1344 if( p_sys->i_use_kerning && i_glyph_index
1348 FT_Get_Kerning( face, i_previous, i_glyph_index,
1349 ft_kerning_default, &delta );
1350 i_pen_x += delta.x >> 6;
1353 p_line->p_glyph_pos[ i ].x = i_pen_x;
1354 p_line->p_glyph_pos[ i ].y = i_pen_y;
1355 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1358 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1362 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1365 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1369 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1370 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1373 FT_Done_Glyph( tmp_glyph );
1376 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1379 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1380 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1381 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1383 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1384 p_line->pp_glyphs[ i ] = NULL;
1386 p_line = NewLine( strlen( psz_string ));
1387 if( p_prev ) p_prev->p_next = p_line;
1388 else p_lines = p_line;
1390 uint32_t *psz_unicode_saved = psz_unicode;
1391 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1395 if( psz_unicode == psz_line_start )
1396 { /* try harder to break that line */
1397 psz_unicode = psz_unicode_saved;
1398 while( psz_unicode > psz_line_start &&
1399 *psz_unicode != '_' && *psz_unicode != '/' &&
1400 *psz_unicode != '\\' && *psz_unicode != '.' )
1405 if( psz_unicode == psz_line_start )
1407 msg_Warn( p_filter, "unbreakable string" );
1412 *psz_unicode = '\n';
1414 psz_unicode = psz_line_start;
1417 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1420 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1421 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1423 i_previous = i_glyph_index;
1424 i_pen_x += glyph->advance.x >> 6;
1428 p_line->i_width = line.xMax;
1429 p_line->i_height = face->size->metrics.height >> 6;
1430 p_line->pp_glyphs[ i ] = NULL;
1431 p_line->i_alpha = i_font_alpha;
1432 p_line->i_red = i_red;
1433 p_line->i_green = i_green;
1434 p_line->i_blue = i_blue;
1435 result.x = __MAX( result.x, line.xMax );
1436 result.y += line.yMax - line.yMin;
1441 p_region_out->i_x = p_region_in->i_x;
1442 p_region_out->i_y = p_region_in->i_y;
1444 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1445 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1447 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1449 free( psz_unicode_orig );
1450 FreeLines( p_lines );
1454 free( psz_unicode_orig );
1455 FreeLines( p_lines );
1456 return VLC_EGENERIC;
1459 #ifdef HAVE_FONTCONFIG
1460 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1461 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1462 bool b_italic, bool b_uline )
1464 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1468 p_style->i_font_size = i_font_size;
1469 p_style->i_font_color = i_font_color;
1470 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1471 p_style->b_italic = b_italic;
1472 p_style->b_bold = b_bold;
1473 p_style->b_underline = b_uline;
1475 p_style->psz_fontname = strdup( psz_fontname );
1480 static void DeleteStyle( ft_style_t *p_style )
1484 free( p_style->psz_fontname );
1489 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1496 if(( s1->i_font_size == s2->i_font_size ) &&
1497 ( s1->i_font_color == s2->i_font_color ) &&
1498 ( s1->b_italic == s2->b_italic ) &&
1499 ( s1->b_bold == s2->b_bold ) &&
1500 ( s1->b_underline == s2->b_underline ) &&
1501 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1508 static void IconvText( filter_t *p_filter, const char *psz_string,
1509 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1511 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1513 /* If memory hasn't been allocated for our output string, allocate it here
1514 * - the calling function must now be responsible for freeing it.
1516 if( !*ppsz_unicode )
1517 *ppsz_unicode = (uint32_t *)
1518 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1520 /* We don't need to handle a NULL pointer in *ppsz_unicode
1521 * if we are instead testing for a non NULL value like we are here */
1525 #if defined(WORDS_BIGENDIAN)
1526 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1528 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1530 if( iconv_handle != (vlc_iconv_t)-1 )
1532 char *p_in_buffer, *p_out_buffer;
1533 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1534 i_in_bytes = strlen( psz_string );
1535 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1536 i_out_bytes_left = i_out_bytes;
1537 p_in_buffer = (char *) psz_string;
1538 p_out_buffer = (char *) *ppsz_unicode;
1539 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1540 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1542 vlc_iconv_close( iconv_handle );
1546 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1547 "bytes left %u", (unsigned)i_in_bytes );
1551 *(uint32_t*)p_out_buffer = 0;
1553 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1558 msg_Warn( p_filter, "unable to do conversion" );
1563 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1564 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1567 ft_style_t *p_style = NULL;
1569 char *psz_fontname = NULL;
1570 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1571 uint32_t i_karaoke_bg_color = i_font_color;
1572 int i_font_size = p_sys->i_font_size;
1574 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1575 &i_font_color, &i_karaoke_bg_color ))
1577 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1578 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1583 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1584 bool b_uline, int i_karaoke_bgcolor,
1585 line_desc_t *p_line, uint32_t *psz_unicode,
1586 int *pi_pen_x, int i_pen_y, int *pi_start,
1587 FT_Vector *p_result )
1592 bool b_first_on_line = true;
1595 int i_pen_x_start = *pi_pen_x;
1597 uint32_t *psz_unicode_start = psz_unicode;
1599 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1601 /* Account for part of line already in position */
1602 for( i=0; i<*pi_start; i++ )
1606 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1607 ft_glyph_bbox_pixels, &glyph_size );
1609 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1610 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1611 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1612 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1618 b_first_on_line = false;
1620 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1626 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1627 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1631 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1632 ft_kerning_default, &delta );
1633 *pi_pen_x += delta.x >> 6;
1635 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1636 p_line->p_glyph_pos[ i ].y = i_pen_y;
1638 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1642 "unable to render text FT_Load_Glyph returned %d", i_error );
1643 p_line->pp_glyphs[ i ] = NULL;
1644 return VLC_EGENERIC;
1646 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1650 "unable to render text FT_Get_Glyph returned %d", i_error );
1651 p_line->pp_glyphs[ i ] = NULL;
1652 return VLC_EGENERIC;
1654 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1655 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1658 FT_Done_Glyph( tmp_glyph );
1663 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1664 p_face->size->metrics.y_scale));
1665 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1666 p_face->size->metrics.y_scale));
1668 p_line->pi_underline_offset[ i ] =
1669 ( aOffset < 0 ) ? -aOffset : aOffset;
1670 p_line->pi_underline_thickness[ i ] =
1671 ( aSize < 0 ) ? -aSize : aSize;
1673 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1674 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1675 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1676 p_line->p_fg_bg_ratio[ i ] = 0x00;
1678 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1679 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1680 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1682 for( ; i >= *pi_start; i-- )
1683 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1686 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1690 if( psz_unicode == psz_unicode_start )
1692 if( b_first_on_line )
1694 msg_Warn( p_filter, "unbreakable string" );
1695 p_line->pp_glyphs[ i ] = NULL;
1696 return VLC_EGENERIC;
1698 *pi_pen_x = i_pen_x_start;
1700 p_line->i_width = line.xMax;
1701 p_line->i_height = __MAX( p_line->i_height,
1702 p_face->size->metrics.height >> 6 );
1703 p_line->pp_glyphs[ i ] = NULL;
1705 p_result->x = __MAX( p_result->x, line.xMax );
1706 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1707 i_yMax - i_yMin ) );
1712 *psz_unicode = '\n';
1714 psz_unicode = psz_unicode_start;
1715 *pi_pen_x = i_pen_x_start;
1723 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1724 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1726 i_previous = i_glyph_index;
1727 *pi_pen_x += p_face->glyph->advance.x >> 6;
1730 p_line->i_width = line.xMax;
1731 p_line->i_height = __MAX( p_line->i_height,
1732 p_face->size->metrics.height >> 6 );
1733 p_line->pp_glyphs[ i ] = NULL;
1735 p_result->x = __MAX( p_result->x, line.xMax );
1736 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1737 line.yMax - line.yMin ) );
1741 /* Get rid of any text processed - if necessary repositioning
1742 * at the start of a new line of text
1746 *psz_unicode_start = '\0';
1748 else if( psz_unicode > psz_unicode_start )
1750 for( i=0; psz_unicode[ i ]; i++ )
1751 psz_unicode_start[ i ] = psz_unicode[ i ];
1752 psz_unicode_start[ i ] = '\0';
1758 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1759 uint32_t **psz_text_out, uint32_t *pi_runs,
1760 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1761 ft_style_t *p_style )
1763 uint32_t i_string_length = 0;
1765 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1766 *psz_text_out += i_string_length;
1768 if( ppp_styles && ppi_run_lengths )
1774 *ppp_styles = (ft_style_t **)
1775 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1777 else if( *pi_runs == 1 )
1779 *ppp_styles = (ft_style_t **)
1780 malloc( *pi_runs * sizeof( ft_style_t * ) );
1783 /* We have just malloc'ed this memory successfully -
1784 * *pi_runs HAS to be within the memory area of *ppp_styles */
1787 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1791 if( *ppi_run_lengths )
1793 *ppi_run_lengths = (uint32_t *)
1794 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1796 else if( *pi_runs == 1 )
1798 *ppi_run_lengths = (uint32_t *)
1799 malloc( *pi_runs * sizeof( uint32_t ) );
1802 /* same remarks here */
1803 if( *ppi_run_lengths )
1805 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1808 /* If we couldn't use the p_style argument due to memory allocation
1809 * problems above, release it here.
1811 if( p_style ) DeleteStyle( p_style );
1814 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1818 for( k=0; k < p_sys->i_font_attachments; k++ )
1820 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1822 FT_Face p_face = NULL;
1824 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1832 bool match = !strcasecmp( p_face->family_name,
1833 p_style->psz_fontname );
1835 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1836 match = match && p_style->b_bold;
1838 match = match && !p_style->b_bold;
1840 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1841 match = match && p_style->b_italic;
1843 match = match && !p_style->b_italic;
1851 FT_Done_Face( p_face );
1856 return VLC_EGENERIC;
1859 static int ProcessLines( filter_t *p_filter,
1864 uint32_t *pi_run_lengths,
1865 ft_style_t **pp_styles,
1866 line_desc_t **pp_lines,
1868 FT_Vector *p_result,
1872 uint32_t *pi_k_run_lengths,
1873 uint32_t *pi_k_durations )
1875 filter_sys_t *p_sys = p_filter->p_sys;
1876 ft_style_t **pp_char_styles;
1877 int *p_new_positions = NULL;
1878 int8_t *p_levels = NULL;
1879 uint8_t *pi_karaoke_bar = NULL;
1883 /* Assign each character in the text string its style explicitly, so that
1884 * after the characters have been shuffled around by Fribidi, we can re-apply
1885 * the styles, and to simplify the calculation of runs within a line.
1887 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1888 if( !pp_char_styles )
1893 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1894 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1895 * we just won't be able to display the progress bar; at least we'll
1901 for( j = 0; j < i_runs; j++ )
1902 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1903 pp_char_styles[ i++ ] = pp_styles[ j ];
1905 #if defined(HAVE_FRIBIDI)
1907 ft_style_t **pp_char_styles_new;
1908 int *p_old_positions;
1909 uint32_t *p_fribidi_string;
1910 int start_pos, pos = 0;
1912 pp_char_styles_new = (ft_style_t **)
1913 malloc( i_len * sizeof( ft_style_t * ));
1915 p_fribidi_string = (uint32_t *)
1916 malloc( (i_len + 1) * sizeof(uint32_t) );
1917 p_old_positions = (int *)
1918 malloc( (i_len + 1) * sizeof( int ) );
1919 p_new_positions = (int *)
1920 malloc( (i_len + 1) * sizeof( int ) );
1921 p_levels = (int8_t *)
1922 malloc( (i_len + 1) * sizeof( int8_t ) );
1924 if( ! pp_char_styles_new ||
1925 ! p_fribidi_string ||
1926 ! p_old_positions ||
1927 ! p_new_positions ||
1931 free( p_old_positions );
1932 free( p_new_positions );
1933 free( p_fribidi_string );
1934 free( pp_char_styles_new );
1935 free( pi_karaoke_bar );
1937 free( pp_char_styles );
1941 /* Do bidi conversion line-by-line */
1944 while(pos < i_len) {
1945 if (psz_text[pos] != '\n')
1947 p_fribidi_string[pos] = psz_text[pos];
1948 pp_char_styles_new[pos] = pp_char_styles[pos];
1949 p_new_positions[pos] = pos;
1954 while(pos < i_len) {
1955 if (psz_text[pos] == '\n')
1959 if (pos > start_pos)
1961 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1962 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1963 pos - start_pos, &base_dir,
1964 (FriBidiChar*)p_fribidi_string + start_pos,
1965 p_new_positions + start_pos,
1967 p_levels + start_pos );
1968 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1970 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1971 p_old_positions[ j - start_pos ] ];
1972 p_new_positions[ j ] += start_pos;
1976 free( p_old_positions );
1977 free( pp_char_styles );
1978 pp_char_styles = pp_char_styles_new;
1979 psz_text = p_fribidi_string;
1980 p_fribidi_string[ i_len ] = 0;
1983 /* Work out the karaoke */
1984 if( pi_karaoke_bar )
1986 int64_t i_last_duration = 0;
1987 int64_t i_duration = 0;
1988 int64_t i_start_pos = 0;
1989 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1991 for( k = 0; k< i_k_runs; k++ )
1993 double fraction = 0.0;
1995 i_duration += pi_k_durations[ k ];
1997 if( i_duration < i_elapsed )
1999 /* Completely finished this run-length -
2000 * let it render normally */
2004 else if( i_elapsed < i_last_duration )
2006 /* Haven't got up to this segment yet -
2007 * render it completely in karaoke BG mode */
2013 /* Partway through this run */
2015 fraction = (double)(i_elapsed - i_last_duration) /
2016 (double)pi_k_durations[ k ];
2018 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2020 double shade = pi_k_run_lengths[ k ] * fraction;
2022 if( p_new_positions )
2023 j = p_new_positions[ i_start_pos + i ];
2025 j = i_start_pos + i;
2027 if( i < (uint32_t)shade )
2028 pi_karaoke_bar[ j ] = 0xff;
2029 else if( (double)i > shade )
2030 pi_karaoke_bar[ j ] = 0x00;
2033 shade -= (int)shade;
2034 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2035 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2039 i_last_duration = i_duration;
2040 i_start_pos += pi_k_run_lengths[ k ];
2044 free( p_new_positions );
2046 FT_Vector tmp_result;
2048 line_desc_t *p_line = NULL;
2049 line_desc_t *p_prev = NULL;
2055 p_result->x = p_result->y = 0;
2056 tmp_result.x = tmp_result.y = 0;
2059 for( k = 0; k <= (uint32_t) i_len; k++ )
2061 if( ( k == (uint32_t) i_len ) ||
2063 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2065 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2067 /* End of the current style run */
2068 FT_Face p_face = NULL;
2071 /* Look for a match amongst our attachments first */
2072 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2076 char *psz_fontfile = NULL;
2078 vlc_mutex_lock( &fb_lock );
2079 if( p_sys->b_fontconfig_ok )
2081 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2082 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2083 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2084 p_style->psz_fontname,
2089 vlc_mutex_unlock( &fb_lock );
2091 if( psz_fontfile && ! *psz_fontfile )
2093 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2094 " so using default font", p_style->psz_fontname,
2095 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2096 (p_style->b_bold ? "(Bold)" :
2097 (p_style->b_italic ? "(Italic)" : ""))) );
2098 free( psz_fontfile );
2099 psz_fontfile = NULL;
2104 if( FT_New_Face( p_sys->p_library,
2105 psz_fontfile, i_idx, &p_face ) )
2107 free( psz_fontfile );
2108 free( pp_char_styles );
2109 #if defined(HAVE_FRIBIDI)
2112 free( pi_karaoke_bar );
2113 return VLC_EGENERIC;
2115 free( psz_fontfile );
2119 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2121 /* We've loaded a font face which is unhelpful for actually
2122 * rendering text - fallback to the default one.
2124 FT_Done_Face( p_face );
2128 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2129 ft_encoding_unicode ) ||
2130 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2131 p_style->i_font_size ) )
2133 if( p_face ) FT_Done_Face( p_face );
2134 free( pp_char_styles );
2135 #if defined(HAVE_FRIBIDI)
2138 free( pi_karaoke_bar );
2139 return VLC_EGENERIC;
2141 p_sys->i_use_kerning =
2142 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2145 uint32_t *psz_unicode = (uint32_t *)
2146 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2149 if( p_face ) FT_Done_Face( p_face );
2150 free( pp_char_styles );
2151 free( psz_unicode );
2152 #if defined(HAVE_FRIBIDI)
2155 free( pi_karaoke_bar );
2158 memcpy( psz_unicode, psz_text + i_prev,
2159 sizeof( uint32_t ) * ( k - i_prev ) );
2160 psz_unicode[ k - i_prev ] = 0;
2161 while( *psz_unicode )
2165 if( !(p_line = NewLine( i_len - i_prev)) )
2167 if( p_face ) FT_Done_Face( p_face );
2168 free( pp_char_styles );
2169 free( psz_unicode );
2170 #if defined(HAVE_FRIBIDI)
2173 free( pi_karaoke_bar );
2176 /* New Color mode only works in YUVA rendering mode --
2177 * (RGB mode has palette constraints on it). We therefore
2178 * need to populate the legacy colour fields also.
2180 p_line->b_new_color_mode = true;
2181 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2182 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2183 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2184 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2185 p_line->p_next = NULL;
2187 i_pen_y += tmp_result.y;
2191 if( p_prev ) p_prev->p_next = p_line;
2192 else *pp_lines = p_line;
2195 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2196 p_style->i_font_color, p_style->b_underline,
2197 p_style->i_karaoke_bg_color,
2198 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2199 &tmp_result ) != VLC_SUCCESS )
2201 if( p_face ) FT_Done_Face( p_face );
2202 free( pp_char_styles );
2203 free( psz_unicode );
2204 #if defined(HAVE_FRIBIDI)
2207 free( pi_karaoke_bar );
2208 return VLC_EGENERIC;
2213 p_result->x = __MAX( p_result->x, tmp_result.x );
2214 p_result->y += tmp_result.y;
2219 if( *psz_unicode == '\n')
2223 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2225 *c_ptr = *(c_ptr+1);
2230 free( psz_unicode );
2231 if( p_face ) FT_Done_Face( p_face );
2235 free( pp_char_styles );
2236 #if defined(HAVE_FRIBIDI)
2241 p_result->x = __MAX( p_result->x, tmp_result.x );
2242 p_result->y += tmp_result.y;
2245 if( pi_karaoke_bar )
2248 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2250 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2252 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2256 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2258 /* 100% BG colour will render faster if we
2259 * instead make it 100% FG colour, so leave
2260 * the ratio alone and copy the value across
2262 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2266 if( pi_karaoke_bar[ i ] & 0x80 )
2268 /* Swap Left and Right sides over for Right aligned
2269 * language text (eg. Arabic, Hebrew)
2271 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2273 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2274 p_line->p_bg_rgb[ k ] = i_tmp;
2276 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2279 /* Jump over the '\n' at the line-end */
2282 free( pi_karaoke_bar );
2288 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2289 subpicture_region_t *p_region_in )
2291 int rv = VLC_SUCCESS;
2292 stream_t *p_sub = NULL;
2293 xml_reader_t *p_xml_reader = NULL;
2295 if( !p_region_in || !p_region_in->psz_html )
2296 return VLC_EGENERIC;
2298 /* Reset the default fontsize in case screen metrics have changed */
2299 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2301 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2302 (uint8_t *) p_region_in->psz_html,
2303 strlen( p_region_in->psz_html ),
2307 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2308 if( p_filter->p_sys->p_xml )
2310 bool b_karaoke = false;
2312 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2315 /* Look for Root Node */
2316 if( xml_ReaderRead( p_xml_reader ) == 1 )
2318 char *psz_node = xml_ReaderName( p_xml_reader );
2320 if( !strcasecmp( "karaoke", psz_node ) )
2322 /* We're going to have to render the text a number
2323 * of times to show the progress marker on the text.
2325 var_SetBool( p_filter, "text-rerender", true );
2328 else if( !strcasecmp( "text", psz_node ) )
2334 /* Only text and karaoke tags are supported */
2335 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2336 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2337 p_xml_reader = NULL;
2349 uint32_t i_runs = 0;
2350 uint32_t i_k_runs = 0;
2351 uint32_t *pi_run_lengths = NULL;
2352 uint32_t *pi_k_run_lengths = NULL;
2353 uint32_t *pi_k_durations = NULL;
2354 ft_style_t **pp_styles = NULL;
2356 line_desc_t *p_lines = NULL;
2358 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2359 sizeof( uint32_t ) );
2364 rv = ProcessNodes( p_filter, p_xml_reader,
2365 p_region_in->p_style, psz_text, &i_len,
2366 &i_runs, &pi_run_lengths, &pp_styles,
2368 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2371 p_region_out->i_x = p_region_in->i_x;
2372 p_region_out->i_y = p_region_in->i_y;
2374 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2376 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2377 pi_run_lengths, pp_styles, &p_lines, &result,
2378 b_karaoke, i_k_runs, pi_k_run_lengths,
2382 for( k=0; k<i_runs; k++)
2383 DeleteStyle( pp_styles[k] );
2385 free( pi_run_lengths );
2388 /* Don't attempt to render text that couldn't be layed out
2391 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2393 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2395 Render( p_filter, p_region_out, p_lines,
2396 result.x, result.y );
2400 RenderYUVA( p_filter, p_region_out, p_lines,
2401 result.x, result.y );
2405 FreeLines( p_lines );
2407 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2410 stream_Delete( p_sub );
2416 static char* FontConfig_Select( FcConfig* priv, const char* family,
2417 bool b_bold, bool b_italic, int *i_idx )
2420 FcPattern *pat, *p_pat;
2424 pat = FcPatternCreate();
2425 if (!pat) return NULL;
2427 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2428 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2429 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2430 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2432 FcDefaultSubstitute( pat );
2434 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2436 FcPatternDestroy( pat );
2440 p_pat = FcFontMatch( priv, pat, &result );
2441 FcPatternDestroy( pat );
2442 if( !p_pat ) return NULL;
2444 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2445 || ( val_b != FcTrue ) )
2447 FcPatternDestroy( p_pat );
2450 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2455 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2457 FcPatternDestroy( p_pat );
2462 if( strcasecmp((const char*)val_s, family ) != 0 )
2463 msg_Warn( p_filter, "fontconfig: selected font family is not"
2464 "the requested one: '%s' != '%s'\n",
2465 (const char*)val_s, family );
2468 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2470 FcPatternDestroy( p_pat );
2474 FcPatternDestroy( p_pat );
2475 return strdup( (const char*)val_s );
2479 static void FreeLine( line_desc_t *p_line )
2482 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2484 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2486 free( p_line->pp_glyphs );
2487 free( p_line->p_glyph_pos );
2488 free( p_line->p_fg_rgb );
2489 free( p_line->p_bg_rgb );
2490 free( p_line->p_fg_bg_ratio );
2491 free( p_line->pi_underline_offset );
2492 free( p_line->pi_underline_thickness );
2496 static void FreeLines( line_desc_t *p_lines )
2498 line_desc_t *p_line, *p_next;
2500 if( !p_lines ) return;
2502 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2504 p_next = p_line->p_next;
2509 static line_desc_t *NewLine( int i_count )
2511 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2513 if( !p_line ) return NULL;
2514 p_line->i_height = 0;
2515 p_line->i_width = 0;
2516 p_line->p_next = NULL;
2518 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2519 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2520 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2521 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2522 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2523 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2524 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2525 if( ( p_line->pp_glyphs == NULL ) ||
2526 ( p_line->p_glyph_pos == NULL ) ||
2527 ( p_line->p_fg_rgb == NULL ) ||
2528 ( p_line->p_bg_rgb == NULL ) ||
2529 ( p_line->p_fg_bg_ratio == NULL ) ||
2530 ( p_line->pi_underline_offset == NULL ) ||
2531 ( p_line->pi_underline_thickness == NULL ) )
2533 free( p_line->pi_underline_thickness );
2534 free( p_line->pi_underline_offset );
2535 free( p_line->p_fg_rgb );
2536 free( p_line->p_bg_rgb );
2537 free( p_line->p_fg_bg_ratio );
2538 free( p_line->p_glyph_pos );
2539 free( p_line->pp_glyphs );
2543 p_line->pp_glyphs[0] = NULL;
2544 p_line->b_new_color_mode = false;
2549 static int GetFontSize( filter_t *p_filter )
2551 filter_sys_t *p_sys = p_filter->p_sys;
2555 if( p_sys->i_default_font_size )
2557 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2558 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2560 i_size = p_sys->i_default_font_size;
2564 var_Get( p_filter, "freetype-rel-fontsize", &val );
2567 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2568 p_filter->p_sys->i_display_height =
2569 p_filter->fmt_out.video.i_height;
2574 msg_Warn( p_filter, "invalid fontsize, using 12" );
2575 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2576 i_size = 12 * val.i_int / 1000;
2583 static int SetFontSize( filter_t *p_filter, int i_size )
2585 filter_sys_t *p_sys = p_filter->p_sys;
2589 i_size = GetFontSize( p_filter );
2591 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2594 p_sys->i_font_size = i_size;
2596 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2598 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2599 return VLC_EGENERIC;
2605 static void YUVFromRGB( uint32_t i_argb,
2606 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2608 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2609 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2610 int i_blue = ( i_argb & 0x000000ff );
2612 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2613 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2614 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2615 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2616 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2617 -585 * i_blue + 4096 + 1048576) >> 13, 240);