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_file( "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 bool b_fontconfig_ok;
271 FcConfig *p_fontconfig;
274 input_attachment_t **pp_font_attachments;
275 int i_font_attachments;
277 vlc_object_t *p_fontbuilder;
280 #define UCHAR uint32_t
281 #define TR_DEFAULT_FONT FC_DEFAULT_FONT
282 #define TR_FONT_STYLE_PTR ft_style_t *
284 #include "text_renderer.h"
286 /*****************************************************************************
287 * Create: allocates osd-text video thread output method
288 *****************************************************************************
289 * This function allocates and initializes a Clone vout method.
290 *****************************************************************************/
291 static int Create( vlc_object_t *p_this )
293 filter_t *p_filter = (filter_t *)p_this;
295 char *psz_fontfile=NULL;
296 char *psz_fontfamily=NULL;
297 int i_error,fontindex;
299 #ifdef HAVE_FONTCONFIG
300 FcPattern *fontpattern, *fontmatch;
305 /* Allocate structure */
306 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
310 p_sys->p_library = 0;
311 p_sys->i_font_size = 0;
312 p_sys->i_display_height = 0;
314 var_Create( p_filter, "freetype-rel-fontsize",
315 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
317 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
318 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
319 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
320 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
321 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
322 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
323 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
326 if( !psz_fontfamily || !*psz_fontfamily )
328 #ifdef HAVE_FONTCONFIG
329 free( psz_fontfamily);
330 psz_fontfamily=strdup( DEFAULT_FONT );
332 free( psz_fontfamily );
333 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
334 if( !psz_fontfamily )
337 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
338 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
340 strcpy( psz_fontfamily, DEFAULT_FONT );
342 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
346 #ifdef HAVE_FONTCONFIG
347 /* Lets find some fontfile from freetype-font variable family */
349 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
351 fontpattern = FcPatternCreate();
352 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
353 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
355 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
357 FcPatternDestroy( fontpattern );
358 free( psz_fontsize );
361 FcDefaultSubstitute( fontpattern );
363 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
365 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
366 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
369 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile);
370 free( psz_fontsize );
372 psz_fontfile = psz_fontfamily;
375 i_error = FT_Init_FreeType( &p_sys->p_library );
378 msg_Err( p_filter, "couldn't initialize freetype" );
382 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
383 fontindex, &p_sys->p_face );
385 if( i_error == FT_Err_Unknown_File_Format )
387 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
392 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
396 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
399 msg_Err( p_filter, "font has no unicode translation table" );
403 #ifdef HAVE_FONTCONFIG
404 p_sys->b_fontconfig_ok = false;
405 p_sys->p_fontconfig = NULL;
406 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
409 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
411 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
414 p_sys->pp_font_attachments = NULL;
415 p_sys->i_font_attachments = 0;
417 p_filter->pf_render_text = RenderText;
418 #ifdef HAVE_FONTCONFIG
419 p_filter->pf_render_html = RenderHtml;
420 FcPatternDestroy( fontmatch );
421 FcPatternDestroy( fontpattern );
423 p_filter->pf_render_html = NULL;
426 free( psz_fontfamily );
427 LoadFontsFromAttachments( p_filter );
432 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
433 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
434 free( psz_fontfile );
439 /*****************************************************************************
440 * Destroy: destroy Clone video thread output method
441 *****************************************************************************
442 * Clean up all data and library connections
443 *****************************************************************************/
444 static void Destroy( vlc_object_t *p_this )
446 filter_t *p_filter = (filter_t *)p_this;
447 filter_sys_t *p_sys = p_filter->p_sys;
449 if( p_sys->pp_font_attachments )
453 for( k = 0; k < p_sys->i_font_attachments; k++ )
454 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
456 free( p_sys->pp_font_attachments );
459 #ifdef HAVE_FONTCONFIG
460 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
463 /* FcFini asserts calling the subfunction FcCacheFini()
464 * even if no other library functions have been made since FcInit(),
465 * so don't call it. */
467 FT_Done_Face( p_sys->p_face );
468 FT_Done_FreeType( p_sys->p_library );
472 #ifdef HAVE_FONTCONFIG
473 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
475 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
477 /* Check for an existing Fontbuilder thread */
478 vlc_mutex_lock( &fb_lock );
479 vlc_object_t *p_fontbuilder =
480 vlc_object_find_name( p_filter->p_libvlc,
481 "fontlist builder", FIND_CHILD );
485 /* Create the FontBuilderThread thread as a child of a top-level
486 * object, so that it can survive the destruction of the
487 * freetype object - the fontlist only needs to be built once,
488 * and calling the fontbuild a second time while the first is
489 * still in progress can cause thread instabilities.
491 * XXX The fontbuilder will be destroy as soon as it is unused.
494 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
495 sizeof(vlc_object_t) );
498 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
499 p_fontbuilder->p_private = NULL;
500 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
502 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
504 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
505 var_SetBool( p_fontbuilder, "build-done", false );
506 var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
507 var_SetBool( p_fontbuilder, "build-joined", false );
509 if( vlc_thread_create( p_fontbuilder,
512 VLC_THREAD_PRIORITY_LOW ) )
514 msg_Warn( p_filter, "fontconfig database builder thread can't "
515 "be launched. Font styling support will be limited." );
522 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
523 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
525 vlc_mutex_unlock( &fb_lock );
526 return p_fontbuilder;
528 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
530 vlc_mutex_lock( &fb_lock );
533 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
535 /* We wait for the thread on the first FontBuilderDetach */
536 if( !var_GetBool( p_fontbuilder, "build-joined" ) )
538 var_SetBool( p_fontbuilder, "build-joined", true );
539 vlc_mutex_unlock( &fb_lock );
540 /* We need to unlock otherwise we may not join (the thread waiting
541 * for the lock). It is safe to unlock as no one else will try a
542 * join and we have a reference on the object) */
543 vlc_thread_join( p_fontbuilder );
544 vlc_mutex_lock( &fb_lock );
546 vlc_object_release( p_fontbuilder );
548 vlc_mutex_unlock( &fb_lock );
550 static void* FontBuilderThread( vlc_object_t *p_this )
552 FcConfig *p_fontconfig = FcInitLoadConfig();
557 int canc = vlc_savecancel ();
559 //msg_Dbg( p_this, "Building font database..." );
560 msg_Dbg( p_this, "Building font database..." );
562 if(! FcConfigBuildFonts( p_fontconfig ))
564 /* Don't destroy the fontconfig object - we won't be able to do
565 * italics or bold or change the font face, but we will still
566 * be able to do underline and change the font size.
568 msg_Err( p_this, "fontconfig database can't be built. "
569 "Font styling won't be available" );
573 msg_Dbg( p_this, "Finished building font database." );
574 msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
576 vlc_mutex_lock( &fb_lock );
577 p_this->p_private = p_fontconfig;
578 vlc_mutex_unlock( &fb_lock );
580 var_SetBool( p_this, "build-done", true );
581 vlc_restorecancel (canc);
585 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
587 filter_sys_t *p_sys = p_filter->p_sys;
589 p_sys->p_fontconfig = p_fontbuilder->p_private;
590 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
592 static void FontBuilderDestructor( vlc_object_t *p_this )
594 FcConfig *p_fontconfig = p_this->p_private;
597 FcConfigDestroy( p_fontconfig );
599 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
600 vlc_value_t oldval, vlc_value_t newval, void *param )
602 filter_t *p_filter = param;
606 vlc_mutex_lock( &fb_lock );
608 FontBuilderGetFcConfig( p_filter, p_this );
610 vlc_mutex_unlock( &fb_lock );
619 /*****************************************************************************
620 * Make any TTF/OTF fonts present in the attachments of the media file
621 * and store them for later use by the FreeType Engine
622 *****************************************************************************/
623 static int LoadFontsFromAttachments( filter_t *p_filter )
625 filter_sys_t *p_sys = p_filter->p_sys;
626 input_thread_t *p_input;
627 input_attachment_t **pp_attachments;
628 int i_attachments_cnt;
630 int rv = VLC_SUCCESS;
632 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
636 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
638 vlc_object_release(p_input);
642 p_sys->i_font_attachments = 0;
643 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
644 if(! p_sys->pp_font_attachments )
647 for( k = 0; k < i_attachments_cnt; k++ )
649 input_attachment_t *p_attach = pp_attachments[k];
651 if( p_sys->pp_font_attachments )
653 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
654 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
655 ( p_attach->i_data > 0 ) &&
656 ( p_attach->p_data != NULL ) )
658 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
662 vlc_input_attachment_Delete( p_attach );
667 vlc_input_attachment_Delete( p_attach );
670 free( pp_attachments );
672 vlc_object_release(p_input);
677 /*****************************************************************************
678 * Render: place string in picture
679 *****************************************************************************
680 * This function merges the previously rendered freetype glyphs into a picture
681 *****************************************************************************/
682 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
683 line_desc_t *p_line, int i_width, int i_height )
685 static const uint8_t pi_gamma[16] =
686 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
687 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
691 int i, x, y, i_pitch;
692 uint8_t i_y; /* YUV values, derived from incoming RGB */
695 /* Create a new subpicture region */
696 memset( &fmt, 0, sizeof(video_format_t) );
697 fmt.i_chroma = VLC_CODEC_YUVP;
699 fmt.i_width = fmt.i_visible_width = i_width + 4;
700 fmt.i_height = fmt.i_visible_height = i_height + 4;
701 if( p_region->fmt.i_visible_width > 0 )
702 fmt.i_visible_width = p_region->fmt.i_visible_width;
703 if( p_region->fmt.i_visible_height > 0 )
704 fmt.i_visible_height = p_region->fmt.i_visible_height;
705 fmt.i_x_offset = fmt.i_y_offset = 0;
707 assert( !p_region->p_picture );
708 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
709 if( !p_region->p_picture )
713 /* Calculate text color components */
714 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
715 25 * p_line->i_blue + 128) >> 8) + 16;
716 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
717 112 * p_line->i_blue + 128) >> 8) + 128;
718 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
719 18 * p_line->i_blue + 128) >> 8) + 128;
722 fmt.p_palette->i_entries = 16;
723 for( i = 0; i < 8; i++ )
725 fmt.p_palette->palette[i][0] = 0;
726 fmt.p_palette->palette[i][1] = 0x80;
727 fmt.p_palette->palette[i][2] = 0x80;
728 fmt.p_palette->palette[i][3] = pi_gamma[i];
729 fmt.p_palette->palette[i][3] =
730 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
732 for( i = 8; i < fmt.p_palette->i_entries; i++ )
734 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
735 fmt.p_palette->palette[i][1] = i_u;
736 fmt.p_palette->palette[i][2] = i_v;
737 fmt.p_palette->palette[i][3] = pi_gamma[i];
738 fmt.p_palette->palette[i][3] =
739 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
742 p_dst = p_region->p_picture->Y_PIXELS;
743 i_pitch = p_region->p_picture->Y_PITCH;
745 /* Initialize the region pixels */
746 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
748 for( ; p_line != NULL; p_line = p_line->p_next )
750 int i_glyph_tmax = 0;
751 int i_bitmap_offset, i_offset, i_align_offset = 0;
752 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
754 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
755 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
758 if( p_line->i_width < i_width )
760 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
762 i_align_offset = i_width - p_line->i_width;
764 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
766 i_align_offset = ( i_width - p_line->i_width ) / 2;
770 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
772 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
774 i_offset = ( p_line->p_glyph_pos[ i ].y +
775 i_glyph_tmax - p_glyph->top + 2 ) *
776 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
779 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
781 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
783 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
785 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
792 /* Outlining (find something better than nearest neighbour filtering ?) */
795 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
796 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
797 uint8_t left, current;
799 for( y = 1; y < (int)fmt.i_height - 1; y++ )
801 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
802 p_dst += p_region->p_picture->Y_PITCH;
805 for( x = 1; x < (int)fmt.i_width - 1; x++ )
808 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
809 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;
813 memset( p_top, 0, fmt.i_width );
819 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
820 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
821 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
822 int i_glyph_tmax, int i_align_offset,
823 uint8_t i_y, uint8_t i_u, uint8_t i_v,
824 subpicture_region_t *p_region)
828 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
830 p_dst_y = p_region->p_picture->Y_PIXELS;
831 p_dst_u = p_region->p_picture->U_PIXELS;
832 p_dst_v = p_region->p_picture->V_PIXELS;
833 p_dst_a = p_region->p_picture->A_PIXELS;
834 i_pitch = p_region->p_picture->A_PITCH;
836 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
837 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
839 for( y = 0; y < i_line_thickness; y++ )
841 int i_extra = p_this_glyph->bitmap.width;
845 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
846 (p_this_glyph_pos->x + p_this_glyph->left);
848 for( x = 0; x < i_extra; x++ )
852 /* break the underline around the tails of any glyphs which cross it */
853 for( z = x - i_line_thickness;
854 z < x + i_line_thickness && b_ok;
857 if( p_next_glyph && ( z >= i_extra ) )
859 int i_row = i_line_offset + p_next_glyph->top + y;
861 if( ( p_next_glyph->bitmap.rows > i_row ) &&
862 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
867 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
869 int i_row = i_line_offset + p_this_glyph->top + y;
871 if( ( p_this_glyph->bitmap.rows > i_row ) &&
872 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
881 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
882 p_dst_u[i_offset+x] = i_u;
883 p_dst_v[i_offset+x] = i_v;
884 p_dst_a[i_offset+x] = 255;
891 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
893 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
894 int i_pitch = p_region->p_picture->A_PITCH;
897 for( ; p_line != NULL; p_line = p_line->p_next )
899 int i_glyph_tmax=0, i = 0;
900 int i_bitmap_offset, i_offset, i_align_offset = 0;
901 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
903 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
904 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
907 if( p_line->i_width < i_width )
909 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
911 i_align_offset = i_width - p_line->i_width;
913 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
915 i_align_offset = ( i_width - p_line->i_width ) / 2;
919 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
921 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
923 i_offset = ( p_line->p_glyph_pos[ i ].y +
924 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
925 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
926 i_align_offset +xoffset;
928 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
930 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
932 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
933 if( p_dst[i_offset+x] <
934 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
936 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
945 /*****************************************************************************
946 * Render: place string in picture
947 *****************************************************************************
948 * This function merges the previously rendered freetype glyphs into a picture
949 *****************************************************************************/
950 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
951 line_desc_t *p_line, int i_width, int i_height )
953 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
955 int i, x, y, i_pitch, i_alpha;
956 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
958 if( i_width == 0 || i_height == 0 )
961 /* Create a new subpicture region */
962 memset( &fmt, 0, sizeof(video_format_t) );
963 fmt.i_chroma = VLC_CODEC_YUVA;
965 fmt.i_width = fmt.i_visible_width = i_width + 6;
966 fmt.i_height = fmt.i_visible_height = i_height + 6;
967 if( p_region->fmt.i_visible_width > 0 )
968 fmt.i_visible_width = p_region->fmt.i_visible_width;
969 if( p_region->fmt.i_visible_height > 0 )
970 fmt.i_visible_height = p_region->fmt.i_visible_height;
971 fmt.i_x_offset = fmt.i_y_offset = 0;
973 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
974 if( !p_region->p_picture )
978 /* Calculate text color components */
979 YUVFromRGB( (p_line->i_red << 16) |
980 (p_line->i_green << 8) |
983 i_alpha = p_line->i_alpha;
985 p_dst_y = p_region->p_picture->Y_PIXELS;
986 p_dst_u = p_region->p_picture->U_PIXELS;
987 p_dst_v = p_region->p_picture->V_PIXELS;
988 p_dst_a = p_region->p_picture->A_PIXELS;
989 i_pitch = p_region->p_picture->A_PITCH;
991 /* Initialize the region pixels */
992 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
994 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
995 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
996 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
997 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
1001 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
1002 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
1003 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
1004 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
1006 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
1007 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1009 DrawBlack( p_line, i_width, p_region, 0, 0);
1010 DrawBlack( p_line, i_width, p_region, -1, 0);
1011 DrawBlack( p_line, i_width, p_region, 0, -1);
1012 DrawBlack( p_line, i_width, p_region, 1, 0);
1013 DrawBlack( p_line, i_width, p_region, 0, 1);
1016 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1018 DrawBlack( p_line, i_width, p_region, -1, -1);
1019 DrawBlack( p_line, i_width, p_region, -1, 1);
1020 DrawBlack( p_line, i_width, p_region, 1, -1);
1021 DrawBlack( p_line, i_width, p_region, 1, 1);
1023 DrawBlack( p_line, i_width, p_region, -2, 0);
1024 DrawBlack( p_line, i_width, p_region, 0, -2);
1025 DrawBlack( p_line, i_width, p_region, 2, 0);
1026 DrawBlack( p_line, i_width, p_region, 0, 2);
1028 DrawBlack( p_line, i_width, p_region, -2, -2);
1029 DrawBlack( p_line, i_width, p_region, -2, 2);
1030 DrawBlack( p_line, i_width, p_region, 2, -2);
1031 DrawBlack( p_line, i_width, p_region, 2, 2);
1033 DrawBlack( p_line, i_width, p_region, -3, 0);
1034 DrawBlack( p_line, i_width, p_region, 0, -3);
1035 DrawBlack( p_line, i_width, p_region, 3, 0);
1036 DrawBlack( p_line, i_width, p_region, 0, 3);
1039 for( ; p_line != NULL; p_line = p_line->p_next )
1041 int i_glyph_tmax = 0;
1042 int i_bitmap_offset, i_offset, i_align_offset = 0;
1043 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1045 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1046 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1049 if( p_line->i_width < i_width )
1051 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1053 i_align_offset = i_width - p_line->i_width;
1055 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1057 i_align_offset = ( i_width - p_line->i_width ) / 2;
1061 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1063 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1065 i_offset = ( p_line->p_glyph_pos[ i ].y +
1066 i_glyph_tmax - p_glyph->top + 3 ) *
1067 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1070 if( p_line->b_new_color_mode )
1072 /* Every glyph can (and in fact must) have its own color */
1073 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1076 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1078 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1080 uint8_t i_y_local = i_y;
1081 uint8_t i_u_local = i_u;
1082 uint8_t i_v_local = i_v;
1084 if( p_line->p_fg_bg_ratio != 0x00 )
1086 int i_split = p_glyph->bitmap.width *
1087 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1091 YUVFromRGB( p_line->p_bg_rgb[ i ],
1092 &i_y_local, &i_u_local, &i_v_local );
1096 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1098 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1099 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1101 p_dst_u[i_offset+x] = i_u;
1102 p_dst_v[i_offset+x] = i_v;
1104 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1105 p_dst_a[i_offset+x] = 0xff;
1108 i_offset += i_pitch;
1111 if( p_line->pi_underline_thickness[ i ] )
1113 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1114 p_line->pi_underline_offset[ i ],
1115 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1116 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1117 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1118 i_glyph_tmax, i_align_offset,
1125 /* Apply the alpha setting */
1126 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1127 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1133 * This function renders a text subpicture region into another one.
1134 * It also calculates the size needed for this string, and renders the
1135 * needed glyphs into memory. It is used as pf_add_string callback in
1136 * the vout method by this module
1138 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1139 subpicture_region_t *p_region_in )
1141 filter_sys_t *p_sys = p_filter->p_sys;
1142 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1143 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1144 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1145 int i_string_length;
1147 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1148 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1158 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1159 psz_string = p_region_in->psz_text;
1160 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1162 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1163 i_scale = val.i_int;
1165 if( p_region_in->p_style )
1167 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1168 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1169 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1173 i_font_color = p_sys->i_font_color;
1174 i_font_alpha = 255 - p_sys->i_font_opacity;
1175 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1178 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1179 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1180 SetFontSize( p_filter, i_font_size );
1182 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1183 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1184 i_blue = i_font_color & 0x000000FF;
1186 result.x = result.y = 0;
1187 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1189 psz_unicode = psz_unicode_orig =
1190 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1191 if( psz_unicode == NULL )
1193 #if defined(WORDS_BIGENDIAN)
1194 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1196 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1198 if( iconv_handle == (vlc_iconv_t)-1 )
1200 msg_Warn( p_filter, "unable to do conversion" );
1206 const char *p_in_buffer = psz_string;
1207 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1208 i_in_bytes = strlen( psz_string );
1209 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1210 i_out_bytes_left = i_out_bytes;
1211 p_out_buffer = (char *)psz_unicode;
1212 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1214 &p_out_buffer, &i_out_bytes_left );
1216 vlc_iconv_close( iconv_handle );
1220 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1221 "bytes left %u", (unsigned)i_in_bytes );
1224 *(uint32_t*)p_out_buffer = 0;
1225 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1228 #if defined(HAVE_FRIBIDI)
1230 uint32_t *p_fribidi_string;
1231 int32_t start_pos, pos = 0;
1233 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1234 if( !p_fribidi_string )
1237 /* Do bidi conversion line-by-line */
1238 while( pos < i_string_length )
1240 while( pos < i_string_length )
1242 i_char = psz_unicode[pos];
1243 if (i_char != '\r' && i_char != '\n')
1245 p_fribidi_string[pos] = i_char;
1249 while( pos < i_string_length )
1251 i_char = psz_unicode[pos];
1252 if (i_char == '\r' || i_char == '\n')
1256 if (pos > start_pos)
1258 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1259 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1262 (FriBidiChar*)p_fribidi_string + start_pos,
1267 free( psz_unicode_orig );
1268 psz_unicode = psz_unicode_orig = p_fribidi_string;
1269 p_fribidi_string[ i_string_length ] = 0;
1273 /* Calculate relative glyph positions and a bounding box for the
1275 if( !(p_line = NewLine( strlen( psz_string ))) )
1278 i_pen_x = i_pen_y = 0;
1280 psz_line_start = psz_unicode;
1282 #define face p_sys->p_face
1283 #define glyph face->glyph
1285 while( *psz_unicode )
1287 i_char = *psz_unicode++;
1288 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1293 if( i_char == '\n' )
1295 psz_line_start = psz_unicode;
1296 if( !(p_next = NewLine( strlen( psz_string ))) )
1298 p_line->p_next = p_next;
1299 p_line->i_width = line.xMax;
1300 p_line->i_height = face->size->metrics.height >> 6;
1301 p_line->pp_glyphs[ i ] = NULL;
1302 p_line->i_alpha = i_font_alpha;
1303 p_line->i_red = i_red;
1304 p_line->i_green = i_green;
1305 p_line->i_blue = i_blue;
1308 result.x = __MAX( result.x, line.xMax );
1309 result.y += face->size->metrics.height >> 6;
1312 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1313 i_pen_y += face->size->metrics.height >> 6;
1315 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1320 i_glyph_index = FT_Get_Char_Index( face, i_char );
1321 if( p_sys->i_use_kerning && i_glyph_index
1325 FT_Get_Kerning( face, i_previous, i_glyph_index,
1326 ft_kerning_default, &delta );
1327 i_pen_x += delta.x >> 6;
1330 p_line->p_glyph_pos[ i ].x = i_pen_x;
1331 p_line->p_glyph_pos[ i ].y = i_pen_y;
1332 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1335 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1339 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1342 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1346 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1347 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1350 FT_Done_Glyph( tmp_glyph );
1353 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1356 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1357 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1358 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1360 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1361 p_line->pp_glyphs[ i ] = NULL;
1363 p_line = NewLine( strlen( psz_string ));
1364 if( p_prev ) p_prev->p_next = p_line;
1365 else p_lines = p_line;
1367 uint32_t *psz_unicode_saved = psz_unicode;
1368 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1372 if( psz_unicode == psz_line_start )
1373 { /* try harder to break that line */
1374 psz_unicode = psz_unicode_saved;
1375 while( psz_unicode > psz_line_start &&
1376 *psz_unicode != '_' && *psz_unicode != '/' &&
1377 *psz_unicode != '\\' && *psz_unicode != '.' )
1382 if( psz_unicode == psz_line_start )
1384 msg_Warn( p_filter, "unbreakable string" );
1389 *psz_unicode = '\n';
1391 psz_unicode = psz_line_start;
1394 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1397 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1398 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1400 i_previous = i_glyph_index;
1401 i_pen_x += glyph->advance.x >> 6;
1405 p_line->i_width = line.xMax;
1406 p_line->i_height = face->size->metrics.height >> 6;
1407 p_line->pp_glyphs[ i ] = NULL;
1408 p_line->i_alpha = i_font_alpha;
1409 p_line->i_red = i_red;
1410 p_line->i_green = i_green;
1411 p_line->i_blue = i_blue;
1412 result.x = __MAX( result.x, line.xMax );
1413 result.y += line.yMax - line.yMin;
1418 p_region_out->i_x = p_region_in->i_x;
1419 p_region_out->i_y = p_region_in->i_y;
1421 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1422 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1424 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1426 free( psz_unicode_orig );
1427 FreeLines( p_lines );
1431 free( psz_unicode_orig );
1432 FreeLines( p_lines );
1433 return VLC_EGENERIC;
1436 #ifdef HAVE_FONTCONFIG
1437 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1438 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1439 bool b_italic, bool b_uline )
1441 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1445 p_style->i_font_size = i_font_size;
1446 p_style->i_font_color = i_font_color;
1447 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1448 p_style->b_italic = b_italic;
1449 p_style->b_bold = b_bold;
1450 p_style->b_underline = b_uline;
1452 p_style->psz_fontname = strdup( psz_fontname );
1457 static void DeleteStyle( ft_style_t *p_style )
1461 free( p_style->psz_fontname );
1466 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1473 if(( s1->i_font_size == s2->i_font_size ) &&
1474 ( s1->i_font_color == s2->i_font_color ) &&
1475 ( s1->b_italic == s2->b_italic ) &&
1476 ( s1->b_bold == s2->b_bold ) &&
1477 ( s1->b_underline == s2->b_underline ) &&
1478 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1485 static void IconvText( filter_t *p_filter, const char *psz_string,
1486 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1488 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1490 /* If memory hasn't been allocated for our output string, allocate it here
1491 * - the calling function must now be responsible for freeing it.
1493 if( !*ppsz_unicode )
1494 *ppsz_unicode = (uint32_t *)
1495 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1497 /* We don't need to handle a NULL pointer in *ppsz_unicode
1498 * if we are instead testing for a non NULL value like we are here */
1502 #if defined(WORDS_BIGENDIAN)
1503 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1505 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1507 if( iconv_handle != (vlc_iconv_t)-1 )
1509 char *p_in_buffer, *p_out_buffer;
1510 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1511 i_in_bytes = strlen( psz_string );
1512 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1513 i_out_bytes_left = i_out_bytes;
1514 p_in_buffer = (char *) psz_string;
1515 p_out_buffer = (char *) *ppsz_unicode;
1516 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1517 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1519 vlc_iconv_close( iconv_handle );
1523 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1524 "bytes left %u", (unsigned)i_in_bytes );
1528 *(uint32_t*)p_out_buffer = 0;
1530 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1535 msg_Warn( p_filter, "unable to do conversion" );
1540 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1541 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1544 ft_style_t *p_style = NULL;
1546 char *psz_fontname = NULL;
1547 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1548 uint32_t i_karaoke_bg_color = i_font_color;
1549 int i_font_size = p_sys->i_font_size;
1551 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1552 &i_font_color, &i_karaoke_bg_color ))
1554 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1555 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1560 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1561 bool b_uline, int i_karaoke_bgcolor,
1562 line_desc_t *p_line, uint32_t *psz_unicode,
1563 int *pi_pen_x, int i_pen_y, int *pi_start,
1564 FT_Vector *p_result )
1569 bool b_first_on_line = true;
1572 int i_pen_x_start = *pi_pen_x;
1574 uint32_t *psz_unicode_start = psz_unicode;
1576 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1578 /* Account for part of line already in position */
1579 for( i=0; i<*pi_start; i++ )
1583 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1584 ft_glyph_bbox_pixels, &glyph_size );
1586 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1587 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1588 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1589 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1595 b_first_on_line = false;
1597 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1603 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1604 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1608 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1609 ft_kerning_default, &delta );
1610 *pi_pen_x += delta.x >> 6;
1612 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1613 p_line->p_glyph_pos[ i ].y = i_pen_y;
1615 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1619 "unable to render text FT_Load_Glyph returned %d", i_error );
1620 p_line->pp_glyphs[ i ] = NULL;
1621 return VLC_EGENERIC;
1623 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1627 "unable to render text FT_Get_Glyph returned %d", i_error );
1628 p_line->pp_glyphs[ i ] = NULL;
1629 return VLC_EGENERIC;
1631 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1632 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1635 FT_Done_Glyph( tmp_glyph );
1640 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1641 p_face->size->metrics.y_scale));
1642 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1643 p_face->size->metrics.y_scale));
1645 p_line->pi_underline_offset[ i ] =
1646 ( aOffset < 0 ) ? -aOffset : aOffset;
1647 p_line->pi_underline_thickness[ i ] =
1648 ( aSize < 0 ) ? -aSize : aSize;
1650 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1651 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1652 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1653 p_line->p_fg_bg_ratio[ i ] = 0x00;
1655 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1656 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1657 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1659 for( ; i >= *pi_start; i-- )
1660 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1663 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1667 if( psz_unicode == psz_unicode_start )
1669 if( b_first_on_line )
1671 msg_Warn( p_filter, "unbreakable string" );
1672 p_line->pp_glyphs[ i ] = NULL;
1673 return VLC_EGENERIC;
1675 *pi_pen_x = i_pen_x_start;
1677 p_line->i_width = line.xMax;
1678 p_line->i_height = __MAX( p_line->i_height,
1679 p_face->size->metrics.height >> 6 );
1680 p_line->pp_glyphs[ i ] = NULL;
1682 p_result->x = __MAX( p_result->x, line.xMax );
1683 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1684 i_yMax - i_yMin ) );
1689 *psz_unicode = '\n';
1691 psz_unicode = psz_unicode_start;
1692 *pi_pen_x = i_pen_x_start;
1700 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1701 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1703 i_previous = i_glyph_index;
1704 *pi_pen_x += p_face->glyph->advance.x >> 6;
1707 p_line->i_width = line.xMax;
1708 p_line->i_height = __MAX( p_line->i_height,
1709 p_face->size->metrics.height >> 6 );
1710 p_line->pp_glyphs[ i ] = NULL;
1712 p_result->x = __MAX( p_result->x, line.xMax );
1713 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1714 line.yMax - line.yMin ) );
1718 /* Get rid of any text processed - if necessary repositioning
1719 * at the start of a new line of text
1723 *psz_unicode_start = '\0';
1725 else if( psz_unicode > psz_unicode_start )
1727 for( i=0; psz_unicode[ i ]; i++ )
1728 psz_unicode_start[ i ] = psz_unicode[ i ];
1729 psz_unicode_start[ i ] = '\0';
1735 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1736 uint32_t **psz_text_out, uint32_t *pi_runs,
1737 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1738 ft_style_t *p_style )
1740 uint32_t i_string_length = 0;
1742 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1743 *psz_text_out += i_string_length;
1745 if( ppp_styles && ppi_run_lengths )
1751 *ppp_styles = (ft_style_t **)
1752 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1754 else if( *pi_runs == 1 )
1756 *ppp_styles = (ft_style_t **)
1757 malloc( *pi_runs * sizeof( ft_style_t * ) );
1760 /* We have just malloc'ed this memory successfully -
1761 * *pi_runs HAS to be within the memory area of *ppp_styles */
1764 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1768 if( *ppi_run_lengths )
1770 *ppi_run_lengths = (uint32_t *)
1771 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1773 else if( *pi_runs == 1 )
1775 *ppi_run_lengths = (uint32_t *)
1776 malloc( *pi_runs * sizeof( uint32_t ) );
1779 /* same remarks here */
1780 if( *ppi_run_lengths )
1782 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1785 /* If we couldn't use the p_style argument due to memory allocation
1786 * problems above, release it here.
1788 if( p_style ) DeleteStyle( p_style );
1791 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1795 for( k=0; k < p_sys->i_font_attachments; k++ )
1797 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1799 FT_Face p_face = NULL;
1801 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1809 bool match = !strcasecmp( p_face->family_name,
1810 p_style->psz_fontname );
1812 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1813 match = match && p_style->b_bold;
1815 match = match && !p_style->b_bold;
1817 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1818 match = match && p_style->b_italic;
1820 match = match && !p_style->b_italic;
1828 FT_Done_Face( p_face );
1833 return VLC_EGENERIC;
1836 static int ProcessLines( filter_t *p_filter,
1841 uint32_t *pi_run_lengths,
1842 ft_style_t **pp_styles,
1843 line_desc_t **pp_lines,
1845 FT_Vector *p_result,
1849 uint32_t *pi_k_run_lengths,
1850 uint32_t *pi_k_durations )
1852 filter_sys_t *p_sys = p_filter->p_sys;
1853 ft_style_t **pp_char_styles;
1854 int *p_new_positions = NULL;
1855 int8_t *p_levels = NULL;
1856 uint8_t *pi_karaoke_bar = NULL;
1860 /* Assign each character in the text string its style explicitly, so that
1861 * after the characters have been shuffled around by Fribidi, we can re-apply
1862 * the styles, and to simplify the calculation of runs within a line.
1864 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1865 if( !pp_char_styles )
1870 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1871 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1872 * we just won't be able to display the progress bar; at least we'll
1878 for( j = 0; j < i_runs; j++ )
1879 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1880 pp_char_styles[ i++ ] = pp_styles[ j ];
1882 #if defined(HAVE_FRIBIDI)
1884 ft_style_t **pp_char_styles_new;
1885 int *p_old_positions;
1886 uint32_t *p_fribidi_string;
1887 int start_pos, pos = 0;
1889 pp_char_styles_new = (ft_style_t **)
1890 malloc( i_len * sizeof( ft_style_t * ));
1892 p_fribidi_string = (uint32_t *)
1893 malloc( (i_len + 1) * sizeof(uint32_t) );
1894 p_old_positions = (int *)
1895 malloc( (i_len + 1) * sizeof( int ) );
1896 p_new_positions = (int *)
1897 malloc( (i_len + 1) * sizeof( int ) );
1898 p_levels = (int8_t *)
1899 malloc( (i_len + 1) * sizeof( int8_t ) );
1901 if( ! pp_char_styles_new ||
1902 ! p_fribidi_string ||
1903 ! p_old_positions ||
1904 ! p_new_positions ||
1908 free( p_old_positions );
1909 free( p_new_positions );
1910 free( p_fribidi_string );
1911 free( pp_char_styles_new );
1912 free( pi_karaoke_bar );
1914 free( pp_char_styles );
1918 /* Do bidi conversion line-by-line */
1921 while(pos < i_len) {
1922 if (psz_text[pos] != '\n')
1924 p_fribidi_string[pos] = psz_text[pos];
1925 pp_char_styles_new[pos] = pp_char_styles[pos];
1926 p_new_positions[pos] = pos;
1931 while(pos < i_len) {
1932 if (psz_text[pos] == '\n')
1936 if (pos > start_pos)
1938 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1939 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1940 pos - start_pos, &base_dir,
1941 (FriBidiChar*)p_fribidi_string + start_pos,
1942 p_new_positions + start_pos,
1944 p_levels + start_pos );
1945 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1947 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1948 p_old_positions[ j - start_pos ] ];
1949 p_new_positions[ j ] += start_pos;
1953 free( p_old_positions );
1954 free( pp_char_styles );
1955 pp_char_styles = pp_char_styles_new;
1956 psz_text = p_fribidi_string;
1957 p_fribidi_string[ i_len ] = 0;
1960 /* Work out the karaoke */
1961 if( pi_karaoke_bar )
1963 int64_t i_last_duration = 0;
1964 int64_t i_duration = 0;
1965 int64_t i_start_pos = 0;
1966 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1968 for( k = 0; k< i_k_runs; k++ )
1970 double fraction = 0.0;
1972 i_duration += pi_k_durations[ k ];
1974 if( i_duration < i_elapsed )
1976 /* Completely finished this run-length -
1977 * let it render normally */
1981 else if( i_elapsed < i_last_duration )
1983 /* Haven't got up to this segment yet -
1984 * render it completely in karaoke BG mode */
1990 /* Partway through this run */
1992 fraction = (double)(i_elapsed - i_last_duration) /
1993 (double)pi_k_durations[ k ];
1995 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1997 double shade = pi_k_run_lengths[ k ] * fraction;
1999 if( p_new_positions )
2000 j = p_new_positions[ i_start_pos + i ];
2002 j = i_start_pos + i;
2004 if( i < (uint32_t)shade )
2005 pi_karaoke_bar[ j ] = 0xff;
2006 else if( (double)i > shade )
2007 pi_karaoke_bar[ j ] = 0x00;
2010 shade -= (int)shade;
2011 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2012 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2016 i_last_duration = i_duration;
2017 i_start_pos += pi_k_run_lengths[ k ];
2021 free( p_new_positions );
2023 FT_Vector tmp_result;
2025 line_desc_t *p_line = NULL;
2026 line_desc_t *p_prev = NULL;
2032 p_result->x = p_result->y = 0;
2033 tmp_result.x = tmp_result.y = 0;
2036 for( k = 0; k <= (uint32_t) i_len; k++ )
2038 if( ( k == (uint32_t) i_len ) ||
2040 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2042 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2044 /* End of the current style run */
2045 FT_Face p_face = NULL;
2048 /* Look for a match amongst our attachments first */
2049 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2053 char *psz_fontfile = NULL;
2055 vlc_mutex_lock( &fb_lock );
2056 if( p_sys->b_fontconfig_ok )
2058 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2059 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2060 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2061 p_style->psz_fontname,
2066 vlc_mutex_unlock( &fb_lock );
2068 if( psz_fontfile && ! *psz_fontfile )
2070 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2071 " so using default font", p_style->psz_fontname,
2072 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2073 (p_style->b_bold ? "(Bold)" :
2074 (p_style->b_italic ? "(Italic)" : ""))) );
2075 free( psz_fontfile );
2076 psz_fontfile = NULL;
2081 if( FT_New_Face( p_sys->p_library,
2082 psz_fontfile, i_idx, &p_face ) )
2084 free( psz_fontfile );
2085 free( pp_char_styles );
2086 #if defined(HAVE_FRIBIDI)
2089 free( pi_karaoke_bar );
2090 return VLC_EGENERIC;
2092 free( psz_fontfile );
2096 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2098 /* We've loaded a font face which is unhelpful for actually
2099 * rendering text - fallback to the default one.
2101 FT_Done_Face( p_face );
2105 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2106 ft_encoding_unicode ) ||
2107 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2108 p_style->i_font_size ) )
2110 if( p_face ) FT_Done_Face( p_face );
2111 free( pp_char_styles );
2112 #if defined(HAVE_FRIBIDI)
2115 free( pi_karaoke_bar );
2116 return VLC_EGENERIC;
2118 p_sys->i_use_kerning =
2119 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2122 uint32_t *psz_unicode = (uint32_t *)
2123 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2126 if( p_face ) FT_Done_Face( p_face );
2127 free( pp_char_styles );
2128 free( psz_unicode );
2129 #if defined(HAVE_FRIBIDI)
2132 free( pi_karaoke_bar );
2135 memcpy( psz_unicode, psz_text + i_prev,
2136 sizeof( uint32_t ) * ( k - i_prev ) );
2137 psz_unicode[ k - i_prev ] = 0;
2138 while( *psz_unicode )
2142 if( !(p_line = NewLine( i_len - i_prev)) )
2144 if( p_face ) FT_Done_Face( p_face );
2145 free( pp_char_styles );
2146 free( psz_unicode );
2147 #if defined(HAVE_FRIBIDI)
2150 free( pi_karaoke_bar );
2153 /* New Color mode only works in YUVA rendering mode --
2154 * (RGB mode has palette constraints on it). We therefore
2155 * need to populate the legacy colour fields also.
2157 p_line->b_new_color_mode = true;
2158 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2159 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2160 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2161 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2162 p_line->p_next = NULL;
2164 i_pen_y += tmp_result.y;
2168 if( p_prev ) p_prev->p_next = p_line;
2169 else *pp_lines = p_line;
2172 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2173 p_style->i_font_color, p_style->b_underline,
2174 p_style->i_karaoke_bg_color,
2175 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2176 &tmp_result ) != VLC_SUCCESS )
2178 if( p_face ) FT_Done_Face( p_face );
2179 free( pp_char_styles );
2180 free( psz_unicode );
2181 #if defined(HAVE_FRIBIDI)
2184 free( pi_karaoke_bar );
2185 return VLC_EGENERIC;
2190 p_result->x = __MAX( p_result->x, tmp_result.x );
2191 p_result->y += tmp_result.y;
2196 if( *psz_unicode == '\n')
2200 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2202 *c_ptr = *(c_ptr+1);
2207 free( psz_unicode );
2208 if( p_face ) FT_Done_Face( p_face );
2212 free( pp_char_styles );
2213 #if defined(HAVE_FRIBIDI)
2218 p_result->x = __MAX( p_result->x, tmp_result.x );
2219 p_result->y += tmp_result.y;
2222 if( pi_karaoke_bar )
2225 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2227 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2229 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2233 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2235 /* 100% BG colour will render faster if we
2236 * instead make it 100% FG colour, so leave
2237 * the ratio alone and copy the value across
2239 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2243 if( pi_karaoke_bar[ i ] & 0x80 )
2245 /* Swap Left and Right sides over for Right aligned
2246 * language text (eg. Arabic, Hebrew)
2248 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2250 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2251 p_line->p_bg_rgb[ k ] = i_tmp;
2253 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2256 /* Jump over the '\n' at the line-end */
2259 free( pi_karaoke_bar );
2265 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2266 subpicture_region_t *p_region_in )
2268 int rv = VLC_SUCCESS;
2269 stream_t *p_sub = NULL;
2270 xml_t *p_xml = NULL;
2271 xml_reader_t *p_xml_reader = NULL;
2273 if( !p_region_in || !p_region_in->psz_html )
2274 return VLC_EGENERIC;
2276 /* Reset the default fontsize in case screen metrics have changed */
2277 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2279 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2280 (uint8_t *) p_region_in->psz_html,
2281 strlen( p_region_in->psz_html ),
2285 p_xml = xml_Create( p_filter );
2288 bool b_karaoke = false;
2290 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2293 /* Look for Root Node */
2294 if( xml_ReaderRead( p_xml_reader ) == 1 )
2296 char *psz_node = xml_ReaderName( p_xml_reader );
2298 if( !strcasecmp( "karaoke", psz_node ) )
2300 /* We're going to have to render the text a number
2301 * of times to show the progress marker on the text.
2303 var_SetBool( p_filter, "text-rerender", true );
2306 else if( !strcasecmp( "text", psz_node ) )
2312 /* Only text and karaoke tags are supported */
2313 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2314 xml_ReaderDelete( p_xml, p_xml_reader );
2315 p_xml_reader = NULL;
2327 uint32_t i_runs = 0;
2328 uint32_t i_k_runs = 0;
2329 uint32_t *pi_run_lengths = NULL;
2330 uint32_t *pi_k_run_lengths = NULL;
2331 uint32_t *pi_k_durations = NULL;
2332 ft_style_t **pp_styles = NULL;
2334 line_desc_t *p_lines = NULL;
2336 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2337 sizeof( uint32_t ) );
2342 rv = ProcessNodes( p_filter, p_xml_reader,
2343 p_region_in->p_style, psz_text, &i_len,
2344 &i_runs, &pi_run_lengths, &pp_styles,
2346 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2349 p_region_out->i_x = p_region_in->i_x;
2350 p_region_out->i_y = p_region_in->i_y;
2352 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2354 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2355 pi_run_lengths, pp_styles, &p_lines, &result,
2356 b_karaoke, i_k_runs, pi_k_run_lengths,
2360 for( k=0; k<i_runs; k++)
2361 DeleteStyle( pp_styles[k] );
2363 free( pi_run_lengths );
2366 /* Don't attempt to render text that couldn't be layed out
2369 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2371 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2373 Render( p_filter, p_region_out, p_lines,
2374 result.x, result.y );
2378 RenderYUVA( p_filter, p_region_out, p_lines,
2379 result.x, result.y );
2383 FreeLines( p_lines );
2385 xml_ReaderDelete( p_xml, p_xml_reader );
2387 xml_Delete( p_xml );
2389 stream_Delete( p_sub );
2395 static char* FontConfig_Select( FcConfig* priv, const char* family,
2396 bool b_bold, bool b_italic, int *i_idx )
2399 FcPattern *pat, *p_pat;
2403 pat = FcPatternCreate();
2404 if (!pat) return NULL;
2406 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2407 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2408 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2409 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2411 FcDefaultSubstitute( pat );
2413 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2415 FcPatternDestroy( pat );
2419 p_pat = FcFontMatch( priv, pat, &result );
2420 FcPatternDestroy( pat );
2421 if( !p_pat ) return NULL;
2423 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2424 || ( val_b != FcTrue ) )
2426 FcPatternDestroy( p_pat );
2429 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2434 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2436 FcPatternDestroy( p_pat );
2441 if( strcasecmp((const char*)val_s, family ) != 0 )
2442 msg_Warn( p_filter, "fontconfig: selected font family is not"
2443 "the requested one: '%s' != '%s'\n",
2444 (const char*)val_s, family );
2447 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2449 FcPatternDestroy( p_pat );
2453 FcPatternDestroy( p_pat );
2454 return strdup( (const char*)val_s );
2458 static void FreeLine( line_desc_t *p_line )
2461 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2463 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2465 free( p_line->pp_glyphs );
2466 free( p_line->p_glyph_pos );
2467 free( p_line->p_fg_rgb );
2468 free( p_line->p_bg_rgb );
2469 free( p_line->p_fg_bg_ratio );
2470 free( p_line->pi_underline_offset );
2471 free( p_line->pi_underline_thickness );
2475 static void FreeLines( line_desc_t *p_lines )
2477 line_desc_t *p_line, *p_next;
2479 if( !p_lines ) return;
2481 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2483 p_next = p_line->p_next;
2488 static line_desc_t *NewLine( int i_count )
2490 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2492 if( !p_line ) return NULL;
2493 p_line->i_height = 0;
2494 p_line->i_width = 0;
2495 p_line->p_next = NULL;
2497 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2498 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2499 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2500 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2501 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2502 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2503 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2504 if( ( p_line->pp_glyphs == NULL ) ||
2505 ( p_line->p_glyph_pos == NULL ) ||
2506 ( p_line->p_fg_rgb == NULL ) ||
2507 ( p_line->p_bg_rgb == NULL ) ||
2508 ( p_line->p_fg_bg_ratio == NULL ) ||
2509 ( p_line->pi_underline_offset == NULL ) ||
2510 ( p_line->pi_underline_thickness == NULL ) )
2512 free( p_line->pi_underline_thickness );
2513 free( p_line->pi_underline_offset );
2514 free( p_line->p_fg_rgb );
2515 free( p_line->p_bg_rgb );
2516 free( p_line->p_fg_bg_ratio );
2517 free( p_line->p_glyph_pos );
2518 free( p_line->pp_glyphs );
2522 p_line->pp_glyphs[0] = NULL;
2523 p_line->b_new_color_mode = false;
2528 static int GetFontSize( filter_t *p_filter )
2530 filter_sys_t *p_sys = p_filter->p_sys;
2534 if( p_sys->i_default_font_size )
2536 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2537 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2539 i_size = p_sys->i_default_font_size;
2543 var_Get( p_filter, "freetype-rel-fontsize", &val );
2546 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2547 p_filter->p_sys->i_display_height =
2548 p_filter->fmt_out.video.i_height;
2553 msg_Warn( p_filter, "invalid fontsize, using 12" );
2554 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2555 i_size = 12 * val.i_int / 1000;
2562 static int SetFontSize( filter_t *p_filter, int i_size )
2564 filter_sys_t *p_sys = p_filter->p_sys;
2568 i_size = GetFontSize( p_filter );
2570 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2573 p_sys->i_font_size = i_size;
2575 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2577 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2578 return VLC_EGENERIC;
2584 static void YUVFromRGB( uint32_t i_argb,
2585 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2587 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2588 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2589 int i_blue = ( i_argb & 0x000000ff );
2591 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2592 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2593 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2594 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2595 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2596 -585 * i_blue + 4096 + 1048576) >> 13, 240);