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>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
42 #include <vlc_input.h>
43 #include <vlc_strings.h>
49 #include FT_FREETYPE_H
51 #define FT_FLOOR(X) ((X & -64) >> 6)
52 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
56 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
57 #define FC_DEFAULT_FONT "Lucida Grande"
58 #elif defined( SYS_BEOS )
59 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
60 #define FC_DEFAULT_FONT "Swiss"
61 #elif defined( WIN32 )
62 #define DEFAULT_FONT "" /* Default font found at run-time */
63 #define FC_DEFAULT_FONT "Arial"
65 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
66 #define FC_DEFAULT_FONT "Serif Bold"
69 #if defined(HAVE_FRIBIDI)
70 #include <fribidi/fribidi.h>
73 #ifdef HAVE_FONTCONFIG
74 #include <fontconfig/fontconfig.h>
79 /*****************************************************************************
81 *****************************************************************************/
82 static int Create ( vlc_object_t * );
83 static void Destroy( vlc_object_t * );
85 #define FONT_TEXT N_("Font")
86 #define FONT_LONGTEXT N_("Filename for the font you want to use")
87 #define FONTSIZE_TEXT N_("Font size in pixels")
88 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
89 "that will be rendered on the video. " \
90 "If set to something different than 0 this option will override the " \
91 "relative font size." )
92 #define OPACITY_TEXT N_("Opacity")
93 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
94 "text that will be rendered on the video. 0 = transparent, " \
95 "255 = totally opaque. " )
96 #define COLOR_TEXT N_("Text default color")
97 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
98 "the video. This must be an hexadecimal (like HTML colors). The first two "\
99 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
100 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
101 #define FONTSIZER_TEXT N_("Relative font size")
102 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
103 "fonts that will be rendered on the video. If absolute font size is set, "\
104 "relative size will be overriden." )
106 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
107 static const char *const ppsz_sizes_text[] = {
108 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
109 #define YUVP_TEXT N_("Use YUVP renderer")
110 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
111 "This option is only needed if you want to encode into DVB subtitles" )
112 #define EFFECT_TEXT N_("Font Effect")
113 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
114 "text to improve its readability." )
116 #define EFFECT_BACKGROUND 1
117 #define EFFECT_OUTLINE 2
118 #define EFFECT_OUTLINE_FAT 3
120 static int const pi_effects[] = { 1, 2, 3 };
121 static const char *const ppsz_effects_text[] = {
122 N_("Background"),N_("Outline"), N_("Fat Outline") };
123 static const int pi_color_values[] = {
124 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
125 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
126 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
128 static const char *const ppsz_color_descriptions[] = {
129 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
130 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
131 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
134 set_shortname( N_("Text renderer"));
135 set_description( N_("Freetype2 font renderer") );
136 set_category( CAT_VIDEO );
137 set_subcategory( SUBCAT_VIDEO_SUBPIC );
139 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
142 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
143 FONTSIZE_LONGTEXT, true );
145 /* opacity valid on 0..255, with default 255 = fully opaque */
146 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
147 OPACITY_TEXT, OPACITY_LONGTEXT, true );
149 /* hook to the color values list, with default 0x00ffffff = white */
150 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
151 COLOR_LONGTEXT, false );
152 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
154 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
155 FONTSIZER_LONGTEXT, false );
156 change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
157 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
158 EFFECT_LONGTEXT, false );
159 change_integer_list( pi_effects, ppsz_effects_text, NULL );
161 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
162 YUVP_LONGTEXT, true );
163 set_capability( "text renderer", 100 );
164 add_shortcut( "text" );
165 set_callbacks( Create, Destroy );
170 /*****************************************************************************
172 *****************************************************************************/
174 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
175 static int RenderText( filter_t *, subpicture_region_t *,
176 subpicture_region_t * );
177 #ifdef HAVE_FONTCONFIG
178 static int RenderHtml( filter_t *, subpicture_region_t *,
179 subpicture_region_t * );
180 static char *FontConfig_Select( FcConfig *, const char *,
185 static int LoadFontsFromAttachments( filter_t *p_filter );
187 static int GetFontSize( filter_t *p_filter );
188 static int SetFontSize( filter_t *, int );
189 static void YUVFromRGB( uint32_t i_argb,
190 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
192 typedef struct line_desc_t line_desc_t;
195 /** NULL-terminated list of glyphs making the string */
196 FT_BitmapGlyph *pp_glyphs;
197 /** list of relative positions for the glyphs */
198 FT_Vector *p_glyph_pos;
199 /** list of RGB information for styled text
200 * -- if the rendering mode supports it (RenderYUVA) and
201 * b_new_color_mode is set, then it becomes possible to
202 * have multicoloured text within the subtitles. */
205 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
206 bool b_new_color_mode;
207 /** underline information -- only supplied if text should be underlined */
208 uint16_t *pi_underline_offset;
209 uint16_t *pi_underline_thickness;
213 int i_red, i_green, i_blue;
218 static line_desc_t *NewLine( int );
223 uint32_t i_font_color; /* ARGB */
224 uint32_t i_karaoke_bg_color; /* ARGB */
231 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
232 static void FreeLines( line_desc_t * );
233 static void FreeLine( line_desc_t * );
235 #ifdef HAVE_FONTCONFIG
236 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock );
237 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
238 static void* FontBuilderThread( vlc_object_t *p_this);
239 static void FontBuilderDestructor( vlc_object_t *p_this );
240 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
241 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
245 /*****************************************************************************
246 * filter_sys_t: freetype local data
247 *****************************************************************************
248 * This structure is part of the video output thread descriptor.
249 * It describes the freetype specific properties of an output thread.
250 *****************************************************************************/
253 FT_Library p_library; /* handle to library */
254 FT_Face p_face; /* handle to face object */
256 uint8_t i_font_opacity;
261 int i_default_font_size;
262 int i_display_height;
263 #ifdef HAVE_FONTCONFIG
264 vlc_mutex_t *p_fontconfig_lock;
265 bool b_fontconfig_ok;
266 FcConfig *p_fontconfig;
269 input_attachment_t **pp_font_attachments;
270 int i_font_attachments;
272 vlc_object_t *p_fontbuilder;
275 #define UCHAR uint32_t
276 #define TR_DEFAULT_FONT FC_DEFAULT_FONT
277 #define TR_FONT_STYLE_PTR ft_style_t *
279 #include "text_renderer.h"
281 /*****************************************************************************
282 * Create: allocates osd-text video thread output method
283 *****************************************************************************
284 * This function allocates and initializes a Clone vout method.
285 *****************************************************************************/
286 static int Create( vlc_object_t *p_this )
288 filter_t *p_filter = (filter_t *)p_this;
290 char *psz_fontfile = NULL;
294 /* Allocate structure */
295 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
299 p_sys->p_library = 0;
300 p_sys->i_font_size = 0;
301 p_sys->i_display_height = 0;
303 var_Create( p_filter, "freetype-font",
304 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
305 var_Create( p_filter, "freetype-fontsize",
306 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
307 var_Create( p_filter, "freetype-rel-fontsize",
308 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
309 var_Create( p_filter, "freetype-opacity",
310 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
311 var_Create( p_filter, "freetype-effect",
312 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
313 var_Get( p_filter, "freetype-opacity", &val );
314 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
315 var_Create( p_filter, "freetype-color",
316 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
317 var_Get( p_filter, "freetype-color", &val );
318 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
319 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
321 /* Look what method was requested */
322 var_Get( p_filter, "freetype-font", &val );
323 psz_fontfile = val.psz_string;
324 if( !psz_fontfile || !*psz_fontfile )
326 free( psz_fontfile );
327 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
331 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
332 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
333 #elif defined(__APPLE__)
334 strcpy( psz_fontfile, DEFAULT_FONT );
336 msg_Err( p_filter, "user didn't specify a font" );
341 i_error = FT_Init_FreeType( &p_sys->p_library );
344 msg_Err( p_filter, "couldn't initialize freetype" );
347 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
349 if( i_error == FT_Err_Unknown_File_Format )
351 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
356 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
360 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
363 msg_Err( p_filter, "font has no unicode translation table" );
367 #ifdef HAVE_FONTCONFIG
368 p_sys->b_fontconfig_ok = false;
369 p_sys->p_fontconfig = NULL;
370 p_sys->p_fontbuilder = FontBuilderAttach( p_filter, &p_sys->p_fontconfig_lock );
373 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
375 var_Get( p_filter, "freetype-fontsize", &val );
376 p_sys->i_default_font_size = val.i_int;
377 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
379 free( psz_fontfile );
381 p_sys->pp_font_attachments = NULL;
382 p_sys->i_font_attachments = 0;
384 p_filter->pf_render_text = RenderText;
385 #ifdef HAVE_FONTCONFIG
386 p_filter->pf_render_html = RenderHtml;
388 p_filter->pf_render_html = NULL;
391 LoadFontsFromAttachments( p_filter );
396 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
397 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
398 free( psz_fontfile );
403 /*****************************************************************************
404 * Destroy: destroy Clone video thread output method
405 *****************************************************************************
406 * Clean up all data and library connections
407 *****************************************************************************/
408 static void Destroy( vlc_object_t *p_this )
410 filter_t *p_filter = (filter_t *)p_this;
411 filter_sys_t *p_sys = p_filter->p_sys;
413 if( p_sys->pp_font_attachments )
417 for( k = 0; k < p_sys->i_font_attachments; k++ )
418 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
420 free( p_sys->pp_font_attachments );
423 #ifdef HAVE_FONTCONFIG
424 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
427 /* FcFini asserts calling the subfunction FcCacheFini()
428 * even if no other library functions have been made since FcInit(),
429 * so don't call it. */
431 FT_Done_Face( p_sys->p_face );
432 FT_Done_FreeType( p_sys->p_library );
436 #ifdef HAVE_FONTCONFIG
437 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock )
439 /* Check for an existing Fontbuilder thread */
440 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
441 vlc_object_t *p_fontbuilder =
442 vlc_object_find_name( p_filter->p_libvlc,
443 "fontlist builder", FIND_CHILD );
447 /* Create the FontBuilderThread thread as a child of a top-level
448 * object, so that it can survive the destruction of the
449 * freetype object - the fontlist only needs to be built once,
450 * and calling the fontbuild a second time while the first is
451 * still in progress can cause thread instabilities.
453 * XXX The fontbuilder will be destroy as soon as it is unused.
456 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
457 sizeof(vlc_object_t) );
460 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
461 p_fontbuilder->p_private = NULL;
462 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
464 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
466 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
467 var_SetBool( p_fontbuilder, "build-done", false );
469 if( vlc_thread_create( p_fontbuilder,
472 VLC_THREAD_PRIORITY_LOW,
475 msg_Warn( p_filter, "fontconfig database builder thread can't "
476 "be launched. Font styling support will be limited." );
482 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
483 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
485 vlc_mutex_unlock( p_lock );
487 return p_fontbuilder;
489 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
491 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
494 const bool b_alive = vlc_object_alive( p_fontbuilder );
496 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
498 /* We wait for the thread on the first FontBuilderDetach */
501 vlc_object_kill( p_fontbuilder );
502 vlc_mutex_unlock( lock );
504 /* We need to unlock otherwise we may not join (the thread waiting
505 * for the lock). It is safe to unlock as no one else will try a
506 * join and we have a reference on the object) */
507 vlc_thread_join( p_fontbuilder );
509 vlc_mutex_lock( lock );
511 vlc_object_release( p_fontbuilder );
513 vlc_mutex_unlock( lock );
515 static void* FontBuilderThread( vlc_object_t *p_this )
517 FcConfig *p_fontconfig = FcInitLoadConfig();
519 vlc_thread_ready( p_this );
524 int canc = vlc_savecancel ();
526 //msg_Dbg( p_this, "Building font database..." );
527 msg_Dbg( p_this, "Building font database..." );
529 if(! FcConfigBuildFonts( p_fontconfig ))
531 /* Don't destroy the fontconfig object - we won't be able to do
532 * italics or bold or change the font face, but we will still
533 * be able to do underline and change the font size.
535 msg_Err( p_this, "fontconfig database can't be built. "
536 "Font styling won't be available" );
540 msg_Dbg( p_this, "Finished building font database." );
541 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
543 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
544 p_this->p_private = p_fontconfig;
545 vlc_mutex_unlock( p_lock );
547 var_SetBool( p_this, "build-done", true );
548 vlc_restorecancel (canc);
552 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
554 filter_sys_t *p_sys = p_filter->p_sys;
556 p_sys->p_fontconfig = p_fontbuilder->p_private;
557 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
559 static void FontBuilderDestructor( vlc_object_t *p_this )
561 FcConfig *p_fontconfig = p_this->p_private;
564 FcConfigDestroy( p_fontconfig );
566 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
567 vlc_value_t oldval, vlc_value_t newval, void *param )
569 filter_t *p_filter = param;
573 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
575 FontBuilderGetFcConfig( p_filter, p_this );
577 vlc_mutex_unlock( p_lock );
586 /*****************************************************************************
587 * Make any TTF/OTF fonts present in the attachments of the media file
588 * and store them for later use by the FreeType Engine
589 *****************************************************************************/
590 static int LoadFontsFromAttachments( filter_t *p_filter )
592 filter_sys_t *p_sys = p_filter->p_sys;
593 input_thread_t *p_input;
594 input_attachment_t **pp_attachments;
595 int i_attachments_cnt;
597 int rv = VLC_SUCCESS;
599 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
603 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
605 vlc_object_release(p_input);
609 p_sys->i_font_attachments = 0;
610 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
611 if(! p_sys->pp_font_attachments )
614 for( k = 0; k < i_attachments_cnt; k++ )
616 input_attachment_t *p_attach = pp_attachments[k];
618 if( p_sys->pp_font_attachments )
620 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
621 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
622 ( p_attach->i_data > 0 ) &&
623 ( p_attach->p_data != NULL ) )
625 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
629 vlc_input_attachment_Delete( p_attach );
634 vlc_input_attachment_Delete( p_attach );
637 free( pp_attachments );
639 vlc_object_release(p_input);
644 /*****************************************************************************
645 * Render: place string in picture
646 *****************************************************************************
647 * This function merges the previously rendered freetype glyphs into a picture
648 *****************************************************************************/
649 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
650 line_desc_t *p_line, int i_width, int i_height )
652 static const uint8_t pi_gamma[16] =
653 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
654 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
658 int i, x, y, i_pitch;
659 uint8_t i_y; /* YUV values, derived from incoming RGB */
662 /* Create a new subpicture region */
663 memset( &fmt, 0, sizeof(video_format_t) );
664 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
666 fmt.i_width = fmt.i_visible_width = i_width + 4;
667 fmt.i_height = fmt.i_visible_height = i_height + 4;
668 if( p_region->fmt.i_visible_width > 0 )
669 fmt.i_visible_width = p_region->fmt.i_visible_width;
670 if( p_region->fmt.i_visible_height > 0 )
671 fmt.i_visible_height = p_region->fmt.i_visible_height;
672 fmt.i_x_offset = fmt.i_y_offset = 0;
674 assert( !p_region->p_picture );
675 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
676 if( !p_region->p_picture )
680 /* Calculate text color components */
681 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
682 25 * p_line->i_blue + 128) >> 8) + 16;
683 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
684 112 * p_line->i_blue + 128) >> 8) + 128;
685 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
686 18 * p_line->i_blue + 128) >> 8) + 128;
689 fmt.p_palette->i_entries = 16;
690 for( i = 0; i < 8; i++ )
692 fmt.p_palette->palette[i][0] = 0;
693 fmt.p_palette->palette[i][1] = 0x80;
694 fmt.p_palette->palette[i][2] = 0x80;
695 fmt.p_palette->palette[i][3] = pi_gamma[i];
696 fmt.p_palette->palette[i][3] =
697 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
699 for( i = 8; i < fmt.p_palette->i_entries; i++ )
701 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
702 fmt.p_palette->palette[i][1] = i_u;
703 fmt.p_palette->palette[i][2] = i_v;
704 fmt.p_palette->palette[i][3] = pi_gamma[i];
705 fmt.p_palette->palette[i][3] =
706 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
709 p_dst = p_region->p_picture->Y_PIXELS;
710 i_pitch = p_region->p_picture->Y_PITCH;
712 /* Initialize the region pixels */
713 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
715 for( ; p_line != NULL; p_line = p_line->p_next )
717 int i_glyph_tmax = 0;
718 int i_bitmap_offset, i_offset, i_align_offset = 0;
719 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
721 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
722 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
725 if( p_line->i_width < i_width )
727 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
729 i_align_offset = i_width - p_line->i_width;
731 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
733 i_align_offset = ( i_width - p_line->i_width ) / 2;
737 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
739 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
741 i_offset = ( p_line->p_glyph_pos[ i ].y +
742 i_glyph_tmax - p_glyph->top + 2 ) *
743 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
746 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
748 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
750 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
752 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
759 /* Outlining (find something better than nearest neighbour filtering ?) */
762 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
763 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
764 uint8_t left, current;
766 for( y = 1; y < (int)fmt.i_height - 1; y++ )
768 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
769 p_dst += p_region->p_picture->Y_PITCH;
772 for( x = 1; x < (int)fmt.i_width - 1; x++ )
775 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
776 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;
780 memset( p_top, 0, fmt.i_width );
786 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
787 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
788 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
789 int i_glyph_tmax, int i_align_offset,
790 uint8_t i_y, uint8_t i_u, uint8_t i_v,
791 subpicture_region_t *p_region)
795 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
797 p_dst_y = p_region->p_picture->Y_PIXELS;
798 p_dst_u = p_region->p_picture->U_PIXELS;
799 p_dst_v = p_region->p_picture->V_PIXELS;
800 p_dst_a = p_region->p_picture->A_PIXELS;
801 i_pitch = p_region->p_picture->A_PITCH;
803 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
804 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
806 for( y = 0; y < i_line_thickness; y++ )
808 int i_extra = p_this_glyph->bitmap.width;
812 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
813 (p_this_glyph_pos->x + p_this_glyph->left);
815 for( x = 0; x < i_extra; x++ )
819 /* break the underline around the tails of any glyphs which cross it */
820 for( z = x - i_line_thickness;
821 z < x + i_line_thickness && b_ok;
824 if( p_next_glyph && ( z >= i_extra ) )
826 int i_row = i_line_offset + p_next_glyph->top + y;
828 if( ( p_next_glyph->bitmap.rows > i_row ) &&
829 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
834 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
836 int i_row = i_line_offset + p_this_glyph->top + y;
838 if( ( p_this_glyph->bitmap.rows > i_row ) &&
839 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
848 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
849 p_dst_u[i_offset+x] = i_u;
850 p_dst_v[i_offset+x] = i_v;
851 p_dst_a[i_offset+x] = 255;
858 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
860 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
861 int i_pitch = p_region->p_picture->A_PITCH;
864 for( ; p_line != NULL; p_line = p_line->p_next )
866 int i_glyph_tmax=0, i = 0;
867 int i_bitmap_offset, i_offset, i_align_offset = 0;
868 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
870 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
871 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
874 if( p_line->i_width < i_width )
876 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
878 i_align_offset = i_width - p_line->i_width;
880 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
882 i_align_offset = ( i_width - p_line->i_width ) / 2;
886 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
888 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
890 i_offset = ( p_line->p_glyph_pos[ i ].y +
891 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
892 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
893 i_align_offset +xoffset;
895 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
897 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
899 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
900 if( p_dst[i_offset+x] <
901 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
903 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
912 /*****************************************************************************
913 * Render: place string in picture
914 *****************************************************************************
915 * This function merges the previously rendered freetype glyphs into a picture
916 *****************************************************************************/
917 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
918 line_desc_t *p_line, int i_width, int i_height )
920 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
922 int i, x, y, i_pitch, i_alpha;
923 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
925 if( i_width == 0 || i_height == 0 )
928 /* Create a new subpicture region */
929 memset( &fmt, 0, sizeof(video_format_t) );
930 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
932 fmt.i_width = fmt.i_visible_width = i_width + 6;
933 fmt.i_height = fmt.i_visible_height = i_height + 6;
934 if( p_region->fmt.i_visible_width > 0 )
935 fmt.i_visible_width = p_region->fmt.i_visible_width;
936 if( p_region->fmt.i_visible_height > 0 )
937 fmt.i_visible_height = p_region->fmt.i_visible_height;
938 fmt.i_x_offset = fmt.i_y_offset = 0;
940 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
941 if( !p_region->p_picture )
945 /* Calculate text color components */
946 YUVFromRGB( (p_line->i_red << 16) |
947 (p_line->i_green << 8) |
950 i_alpha = p_line->i_alpha;
952 p_dst_y = p_region->p_picture->Y_PIXELS;
953 p_dst_u = p_region->p_picture->U_PIXELS;
954 p_dst_v = p_region->p_picture->V_PIXELS;
955 p_dst_a = p_region->p_picture->A_PIXELS;
956 i_pitch = p_region->p_picture->A_PITCH;
958 /* Initialize the region pixels */
959 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
961 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
962 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
963 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
964 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
968 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
969 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
970 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
971 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
973 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
974 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
976 DrawBlack( p_line, i_width, p_region, 0, 0);
977 DrawBlack( p_line, i_width, p_region, -1, 0);
978 DrawBlack( p_line, i_width, p_region, 0, -1);
979 DrawBlack( p_line, i_width, p_region, 1, 0);
980 DrawBlack( p_line, i_width, p_region, 0, 1);
983 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
985 DrawBlack( p_line, i_width, p_region, -1, -1);
986 DrawBlack( p_line, i_width, p_region, -1, 1);
987 DrawBlack( p_line, i_width, p_region, 1, -1);
988 DrawBlack( p_line, i_width, p_region, 1, 1);
990 DrawBlack( p_line, i_width, p_region, -2, 0);
991 DrawBlack( p_line, i_width, p_region, 0, -2);
992 DrawBlack( p_line, i_width, p_region, 2, 0);
993 DrawBlack( p_line, i_width, p_region, 0, 2);
995 DrawBlack( p_line, i_width, p_region, -2, -2);
996 DrawBlack( p_line, i_width, p_region, -2, 2);
997 DrawBlack( p_line, i_width, p_region, 2, -2);
998 DrawBlack( p_line, i_width, p_region, 2, 2);
1000 DrawBlack( p_line, i_width, p_region, -3, 0);
1001 DrawBlack( p_line, i_width, p_region, 0, -3);
1002 DrawBlack( p_line, i_width, p_region, 3, 0);
1003 DrawBlack( p_line, i_width, p_region, 0, 3);
1006 for( ; p_line != NULL; p_line = p_line->p_next )
1008 int i_glyph_tmax = 0;
1009 int i_bitmap_offset, i_offset, i_align_offset = 0;
1010 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1012 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1013 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1016 if( p_line->i_width < i_width )
1018 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1020 i_align_offset = i_width - p_line->i_width;
1022 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1024 i_align_offset = ( i_width - p_line->i_width ) / 2;
1028 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1030 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1032 i_offset = ( p_line->p_glyph_pos[ i ].y +
1033 i_glyph_tmax - p_glyph->top + 3 ) *
1034 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1037 if( p_line->b_new_color_mode )
1039 /* Every glyph can (and in fact must) have its own color */
1040 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1043 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1045 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1047 uint8_t i_y_local = i_y;
1048 uint8_t i_u_local = i_u;
1049 uint8_t i_v_local = i_v;
1051 if( p_line->p_fg_bg_ratio != 0x00 )
1053 int i_split = p_glyph->bitmap.width *
1054 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1058 YUVFromRGB( p_line->p_bg_rgb[ i ],
1059 &i_y_local, &i_u_local, &i_v_local );
1063 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1065 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1066 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1068 p_dst_u[i_offset+x] = i_u;
1069 p_dst_v[i_offset+x] = i_v;
1071 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1072 p_dst_a[i_offset+x] = 0xff;
1075 i_offset += i_pitch;
1078 if( p_line->pi_underline_thickness[ i ] )
1080 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1081 p_line->pi_underline_offset[ i ],
1082 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1083 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1084 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1085 i_glyph_tmax, i_align_offset,
1092 /* Apply the alpha setting */
1093 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1094 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1100 * This function renders a text subpicture region into another one.
1101 * It also calculates the size needed for this string, and renders the
1102 * needed glyphs into memory. It is used as pf_add_string callback in
1103 * the vout method by this module
1105 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1106 subpicture_region_t *p_region_in )
1108 filter_sys_t *p_sys = p_filter->p_sys;
1109 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1110 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1111 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1112 int i_string_length;
1114 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1115 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1125 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1126 psz_string = p_region_in->psz_text;
1127 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1129 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1130 i_scale = val.i_int;
1132 if( p_region_in->p_style )
1134 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1135 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1136 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1140 i_font_color = p_sys->i_font_color;
1141 i_font_alpha = 255 - p_sys->i_font_opacity;
1142 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1145 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1146 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1147 SetFontSize( p_filter, i_font_size );
1149 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1150 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1151 i_blue = i_font_color & 0x000000FF;
1153 result.x = result.y = 0;
1154 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1156 psz_unicode = psz_unicode_orig =
1157 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1158 if( psz_unicode == NULL )
1160 #if defined(WORDS_BIGENDIAN)
1161 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1163 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1165 if( iconv_handle == (vlc_iconv_t)-1 )
1167 msg_Warn( p_filter, "unable to do conversion" );
1173 const char *p_in_buffer = psz_string;
1174 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1175 i_in_bytes = strlen( psz_string );
1176 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1177 i_out_bytes_left = i_out_bytes;
1178 p_out_buffer = (char *)psz_unicode;
1179 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1181 &p_out_buffer, &i_out_bytes_left );
1183 vlc_iconv_close( iconv_handle );
1187 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1188 "bytes left %u", (unsigned)i_in_bytes );
1191 *(uint32_t*)p_out_buffer = 0;
1192 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1195 #if defined(HAVE_FRIBIDI)
1197 uint32_t *p_fribidi_string;
1198 int32_t start_pos, pos = 0;
1200 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1201 if( !p_fribidi_string )
1204 /* Do bidi conversion line-by-line */
1205 while( pos < i_string_length )
1207 while( pos < i_string_length )
1209 i_char = psz_unicode[pos];
1210 if (i_char != '\r' && i_char != '\n')
1212 p_fribidi_string[pos] = i_char;
1216 while( pos < i_string_length )
1218 i_char = psz_unicode[pos];
1219 if (i_char == '\r' || i_char == '\n')
1223 if (pos > start_pos)
1225 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1226 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1229 (FriBidiChar*)p_fribidi_string + start_pos,
1234 free( psz_unicode_orig );
1235 psz_unicode = psz_unicode_orig = p_fribidi_string;
1236 p_fribidi_string[ i_string_length ] = 0;
1240 /* Calculate relative glyph positions and a bounding box for the
1242 if( !(p_line = NewLine( strlen( psz_string ))) )
1245 i_pen_x = i_pen_y = 0;
1247 psz_line_start = psz_unicode;
1249 #define face p_sys->p_face
1250 #define glyph face->glyph
1252 while( *psz_unicode )
1254 i_char = *psz_unicode++;
1255 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1260 if( i_char == '\n' )
1262 psz_line_start = psz_unicode;
1263 if( !(p_next = NewLine( strlen( psz_string ))) )
1265 p_line->p_next = p_next;
1266 p_line->i_width = line.xMax;
1267 p_line->i_height = face->size->metrics.height >> 6;
1268 p_line->pp_glyphs[ i ] = NULL;
1269 p_line->i_alpha = i_font_alpha;
1270 p_line->i_red = i_red;
1271 p_line->i_green = i_green;
1272 p_line->i_blue = i_blue;
1275 result.x = __MAX( result.x, line.xMax );
1276 result.y += face->size->metrics.height >> 6;
1279 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1280 i_pen_y += face->size->metrics.height >> 6;
1282 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1287 i_glyph_index = FT_Get_Char_Index( face, i_char );
1288 if( p_sys->i_use_kerning && i_glyph_index
1292 FT_Get_Kerning( face, i_previous, i_glyph_index,
1293 ft_kerning_default, &delta );
1294 i_pen_x += delta.x >> 6;
1297 p_line->p_glyph_pos[ i ].x = i_pen_x;
1298 p_line->p_glyph_pos[ i ].y = i_pen_y;
1299 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1302 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1306 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1309 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1313 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1314 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1317 FT_Done_Glyph( tmp_glyph );
1320 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1323 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1324 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1325 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1327 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1328 p_line->pp_glyphs[ i ] = NULL;
1330 p_line = NewLine( strlen( psz_string ));
1331 if( p_prev ) p_prev->p_next = p_line;
1332 else p_lines = p_line;
1334 uint32_t *psz_unicode_saved = psz_unicode;
1335 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1339 if( psz_unicode == psz_line_start )
1340 { /* try harder to break that line */
1341 psz_unicode = psz_unicode_saved;
1342 while( psz_unicode > psz_line_start &&
1343 *psz_unicode != '_' && *psz_unicode != '/' &&
1344 *psz_unicode != '\\' && *psz_unicode != '.' )
1349 if( psz_unicode == psz_line_start )
1351 msg_Warn( p_filter, "unbreakable string" );
1356 *psz_unicode = '\n';
1358 psz_unicode = psz_line_start;
1361 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1364 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1365 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1367 i_previous = i_glyph_index;
1368 i_pen_x += glyph->advance.x >> 6;
1372 p_line->i_width = line.xMax;
1373 p_line->i_height = face->size->metrics.height >> 6;
1374 p_line->pp_glyphs[ i ] = NULL;
1375 p_line->i_alpha = i_font_alpha;
1376 p_line->i_red = i_red;
1377 p_line->i_green = i_green;
1378 p_line->i_blue = i_blue;
1379 result.x = __MAX( result.x, line.xMax );
1380 result.y += line.yMax - line.yMin;
1385 p_region_out->i_x = p_region_in->i_x;
1386 p_region_out->i_y = p_region_in->i_y;
1388 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1389 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1391 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1393 free( psz_unicode_orig );
1394 FreeLines( p_lines );
1398 free( psz_unicode_orig );
1399 FreeLines( p_lines );
1400 return VLC_EGENERIC;
1403 #ifdef HAVE_FONTCONFIG
1404 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1405 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1406 bool b_italic, bool b_uline )
1408 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1412 p_style->i_font_size = i_font_size;
1413 p_style->i_font_color = i_font_color;
1414 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1415 p_style->b_italic = b_italic;
1416 p_style->b_bold = b_bold;
1417 p_style->b_underline = b_uline;
1419 p_style->psz_fontname = strdup( psz_fontname );
1424 static void DeleteStyle( ft_style_t *p_style )
1428 free( p_style->psz_fontname );
1433 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1440 if(( s1->i_font_size == s2->i_font_size ) &&
1441 ( s1->i_font_color == s2->i_font_color ) &&
1442 ( s1->b_italic == s2->b_italic ) &&
1443 ( s1->b_bold == s2->b_bold ) &&
1444 ( s1->b_underline == s2->b_underline ) &&
1445 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1452 static void IconvText( filter_t *p_filter, const char *psz_string,
1453 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1455 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1457 /* If memory hasn't been allocated for our output string, allocate it here
1458 * - the calling function must now be responsible for freeing it.
1460 if( !*ppsz_unicode )
1461 *ppsz_unicode = (uint32_t *)
1462 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1464 /* We don't need to handle a NULL pointer in *ppsz_unicode
1465 * if we are instead testing for a non NULL value like we are here */
1469 #if defined(WORDS_BIGENDIAN)
1470 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1472 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1474 if( iconv_handle != (vlc_iconv_t)-1 )
1476 char *p_in_buffer, *p_out_buffer;
1477 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1478 i_in_bytes = strlen( psz_string );
1479 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1480 i_out_bytes_left = i_out_bytes;
1481 p_in_buffer = (char *) psz_string;
1482 p_out_buffer = (char *) *ppsz_unicode;
1483 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1484 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1486 vlc_iconv_close( iconv_handle );
1490 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1491 "bytes left %u", (unsigned)i_in_bytes );
1495 *(uint32_t*)p_out_buffer = 0;
1497 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1502 msg_Warn( p_filter, "unable to do conversion" );
1507 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1508 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1511 ft_style_t *p_style = NULL;
1513 char *psz_fontname = NULL;
1514 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1515 uint32_t i_karaoke_bg_color = i_font_color;
1516 int i_font_size = p_sys->i_font_size;
1518 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1519 &i_font_color, &i_karaoke_bg_color ))
1521 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1522 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1527 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1528 bool b_uline, int i_karaoke_bgcolor,
1529 line_desc_t *p_line, uint32_t *psz_unicode,
1530 int *pi_pen_x, int i_pen_y, int *pi_start,
1531 FT_Vector *p_result )
1536 bool b_first_on_line = true;
1539 int i_pen_x_start = *pi_pen_x;
1541 uint32_t *psz_unicode_start = psz_unicode;
1543 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1545 /* Account for part of line already in position */
1546 for( i=0; i<*pi_start; i++ )
1550 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1551 ft_glyph_bbox_pixels, &glyph_size );
1553 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1554 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1555 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1556 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1562 b_first_on_line = false;
1564 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1570 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1571 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1575 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1576 ft_kerning_default, &delta );
1577 *pi_pen_x += delta.x >> 6;
1579 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1580 p_line->p_glyph_pos[ i ].y = i_pen_y;
1582 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1586 "unable to render text FT_Load_Glyph returned %d", i_error );
1587 p_line->pp_glyphs[ i ] = NULL;
1588 return VLC_EGENERIC;
1590 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1594 "unable to render text FT_Get_Glyph returned %d", i_error );
1595 p_line->pp_glyphs[ i ] = NULL;
1596 return VLC_EGENERIC;
1598 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1599 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1602 FT_Done_Glyph( tmp_glyph );
1607 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1608 p_face->size->metrics.y_scale));
1609 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1610 p_face->size->metrics.y_scale));
1612 p_line->pi_underline_offset[ i ] =
1613 ( aOffset < 0 ) ? -aOffset : aOffset;
1614 p_line->pi_underline_thickness[ i ] =
1615 ( aSize < 0 ) ? -aSize : aSize;
1617 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1618 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1619 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1620 p_line->p_fg_bg_ratio[ i ] = 0x00;
1622 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1623 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1624 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1626 for( ; i >= *pi_start; i-- )
1627 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1630 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1634 if( psz_unicode == psz_unicode_start )
1636 if( b_first_on_line )
1638 msg_Warn( p_filter, "unbreakable string" );
1639 p_line->pp_glyphs[ i ] = NULL;
1640 return VLC_EGENERIC;
1642 *pi_pen_x = i_pen_x_start;
1644 p_line->i_width = line.xMax;
1645 p_line->i_height = __MAX( p_line->i_height,
1646 p_face->size->metrics.height >> 6 );
1647 p_line->pp_glyphs[ i ] = NULL;
1649 p_result->x = __MAX( p_result->x, line.xMax );
1650 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1651 i_yMax - i_yMin ) );
1656 *psz_unicode = '\n';
1658 psz_unicode = psz_unicode_start;
1659 *pi_pen_x = i_pen_x_start;
1667 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1668 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1670 i_previous = i_glyph_index;
1671 *pi_pen_x += p_face->glyph->advance.x >> 6;
1674 p_line->i_width = line.xMax;
1675 p_line->i_height = __MAX( p_line->i_height,
1676 p_face->size->metrics.height >> 6 );
1677 p_line->pp_glyphs[ i ] = NULL;
1679 p_result->x = __MAX( p_result->x, line.xMax );
1680 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1681 line.yMax - line.yMin ) );
1685 /* Get rid of any text processed - if necessary repositioning
1686 * at the start of a new line of text
1690 *psz_unicode_start = '\0';
1692 else if( psz_unicode > psz_unicode_start )
1694 for( i=0; psz_unicode[ i ]; i++ )
1695 psz_unicode_start[ i ] = psz_unicode[ i ];
1696 psz_unicode_start[ i ] = '\0';
1702 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1703 uint32_t **psz_text_out, uint32_t *pi_runs,
1704 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1705 ft_style_t *p_style )
1707 uint32_t i_string_length = 0;
1709 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1710 *psz_text_out += i_string_length;
1712 if( ppp_styles && ppi_run_lengths )
1718 *ppp_styles = (ft_style_t **)
1719 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1721 else if( *pi_runs == 1 )
1723 *ppp_styles = (ft_style_t **)
1724 malloc( *pi_runs * sizeof( ft_style_t * ) );
1727 /* We have just malloc'ed this memory successfully -
1728 * *pi_runs HAS to be within the memory area of *ppp_styles */
1731 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1735 if( *ppi_run_lengths )
1737 *ppi_run_lengths = (uint32_t *)
1738 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1740 else if( *pi_runs == 1 )
1742 *ppi_run_lengths = (uint32_t *)
1743 malloc( *pi_runs * sizeof( uint32_t ) );
1746 /* same remarks here */
1747 if( *ppi_run_lengths )
1749 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1752 /* If we couldn't use the p_style argument due to memory allocation
1753 * problems above, release it here.
1755 if( p_style ) DeleteStyle( p_style );
1758 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1762 for( k=0; k < p_sys->i_font_attachments; k++ )
1764 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1766 FT_Face p_face = NULL;
1768 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1776 bool match = !strcasecmp( p_face->family_name,
1777 p_style->psz_fontname );
1779 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1780 match = match && p_style->b_bold;
1782 match = match && !p_style->b_bold;
1784 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1785 match = match && p_style->b_italic;
1787 match = match && !p_style->b_italic;
1795 FT_Done_Face( p_face );
1800 return VLC_EGENERIC;
1803 static int ProcessLines( filter_t *p_filter,
1808 uint32_t *pi_run_lengths,
1809 ft_style_t **pp_styles,
1810 line_desc_t **pp_lines,
1812 FT_Vector *p_result,
1816 uint32_t *pi_k_run_lengths,
1817 uint32_t *pi_k_durations )
1819 filter_sys_t *p_sys = p_filter->p_sys;
1820 ft_style_t **pp_char_styles;
1821 int *p_new_positions = NULL;
1822 int8_t *p_levels = NULL;
1823 uint8_t *pi_karaoke_bar = NULL;
1827 /* Assign each character in the text string its style explicitly, so that
1828 * after the characters have been shuffled around by Fribidi, we can re-apply
1829 * the styles, and to simplify the calculation of runs within a line.
1831 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1832 if( !pp_char_styles )
1837 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1838 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1839 * we just won't be able to display the progress bar; at least we'll
1845 for( j = 0; j < i_runs; j++ )
1846 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1847 pp_char_styles[ i++ ] = pp_styles[ j ];
1849 #if defined(HAVE_FRIBIDI)
1851 ft_style_t **pp_char_styles_new;
1852 int *p_old_positions;
1853 uint32_t *p_fribidi_string;
1854 int start_pos, pos = 0;
1856 pp_char_styles_new = (ft_style_t **)
1857 malloc( i_len * sizeof( ft_style_t * ));
1859 p_fribidi_string = (uint32_t *)
1860 malloc( (i_len + 1) * sizeof(uint32_t) );
1861 p_old_positions = (int *)
1862 malloc( (i_len + 1) * sizeof( int ) );
1863 p_new_positions = (int *)
1864 malloc( (i_len + 1) * sizeof( int ) );
1865 p_levels = (int8_t *)
1866 malloc( (i_len + 1) * sizeof( int8_t ) );
1868 if( ! pp_char_styles_new ||
1869 ! p_fribidi_string ||
1870 ! p_old_positions ||
1871 ! p_new_positions ||
1875 free( p_old_positions );
1876 free( p_new_positions );
1877 free( p_fribidi_string );
1878 free( pp_char_styles_new );
1879 free( pi_karaoke_bar );
1881 free( pp_char_styles );
1885 /* Do bidi conversion line-by-line */
1888 while(pos < i_len) {
1889 if (psz_text[pos] != '\n')
1891 p_fribidi_string[pos] = psz_text[pos];
1892 pp_char_styles_new[pos] = pp_char_styles[pos];
1893 p_new_positions[pos] = pos;
1898 while(pos < i_len) {
1899 if (psz_text[pos] == '\n')
1903 if (pos > start_pos)
1905 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1906 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1907 pos - start_pos, &base_dir,
1908 (FriBidiChar*)p_fribidi_string + start_pos,
1909 p_new_positions + start_pos,
1911 p_levels + start_pos );
1912 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1914 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1915 p_old_positions[ j - start_pos ] ];
1916 p_new_positions[ j ] += start_pos;
1920 free( p_old_positions );
1921 free( pp_char_styles );
1922 pp_char_styles = pp_char_styles_new;
1923 psz_text = p_fribidi_string;
1924 p_fribidi_string[ i_len ] = 0;
1927 /* Work out the karaoke */
1928 if( pi_karaoke_bar )
1930 int64_t i_last_duration = 0;
1931 int64_t i_duration = 0;
1932 int64_t i_start_pos = 0;
1933 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1935 for( k = 0; k< i_k_runs; k++ )
1937 double fraction = 0.0;
1939 i_duration += pi_k_durations[ k ];
1941 if( i_duration < i_elapsed )
1943 /* Completely finished this run-length -
1944 * let it render normally */
1948 else if( i_elapsed < i_last_duration )
1950 /* Haven't got up to this segment yet -
1951 * render it completely in karaoke BG mode */
1957 /* Partway through this run */
1959 fraction = (double)(i_elapsed - i_last_duration) /
1960 (double)pi_k_durations[ k ];
1962 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1964 double shade = pi_k_run_lengths[ k ] * fraction;
1966 if( p_new_positions )
1967 j = p_new_positions[ i_start_pos + i ];
1969 j = i_start_pos + i;
1971 if( i < (uint32_t)shade )
1972 pi_karaoke_bar[ j ] = 0xff;
1973 else if( (double)i > shade )
1974 pi_karaoke_bar[ j ] = 0x00;
1977 shade -= (int)shade;
1978 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1979 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1983 i_last_duration = i_duration;
1984 i_start_pos += pi_k_run_lengths[ k ];
1988 free( p_new_positions );
1990 FT_Vector tmp_result;
1992 line_desc_t *p_line = NULL;
1993 line_desc_t *p_prev = NULL;
1999 p_result->x = p_result->y = 0;
2000 tmp_result.x = tmp_result.y = 0;
2003 for( k = 0; k <= (uint32_t) i_len; k++ )
2005 if( ( k == (uint32_t) i_len ) ||
2007 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2009 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2011 /* End of the current style run */
2012 FT_Face p_face = NULL;
2015 /* Look for a match amongst our attachments first */
2016 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2020 char *psz_fontfile = NULL;
2022 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2023 if( p_sys->b_fontconfig_ok )
2025 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2026 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2027 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2028 p_style->psz_fontname,
2033 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2035 if( psz_fontfile && ! *psz_fontfile )
2037 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2038 " so using default font", p_style->psz_fontname,
2039 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2040 (p_style->b_bold ? "(Bold)" :
2041 (p_style->b_italic ? "(Italic)" : ""))) );
2042 free( psz_fontfile );
2043 psz_fontfile = NULL;
2048 if( FT_New_Face( p_sys->p_library,
2049 psz_fontfile, i_idx, &p_face ) )
2051 free( psz_fontfile );
2052 free( pp_char_styles );
2053 #if defined(HAVE_FRIBIDI)
2056 free( pi_karaoke_bar );
2057 return VLC_EGENERIC;
2059 free( psz_fontfile );
2063 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2065 /* We've loaded a font face which is unhelpful for actually
2066 * rendering text - fallback to the default one.
2068 FT_Done_Face( p_face );
2072 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2073 ft_encoding_unicode ) ||
2074 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2075 p_style->i_font_size ) )
2077 if( p_face ) FT_Done_Face( p_face );
2078 free( pp_char_styles );
2079 #if defined(HAVE_FRIBIDI)
2082 free( pi_karaoke_bar );
2083 return VLC_EGENERIC;
2085 p_sys->i_use_kerning =
2086 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2089 uint32_t *psz_unicode = (uint32_t *)
2090 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2093 if( p_face ) FT_Done_Face( p_face );
2094 free( pp_char_styles );
2095 free( psz_unicode );
2096 #if defined(HAVE_FRIBIDI)
2099 free( pi_karaoke_bar );
2102 memcpy( psz_unicode, psz_text + i_prev,
2103 sizeof( uint32_t ) * ( k - i_prev ) );
2104 psz_unicode[ k - i_prev ] = 0;
2105 while( *psz_unicode )
2109 if( !(p_line = NewLine( i_len - i_prev)) )
2111 if( p_face ) FT_Done_Face( p_face );
2112 free( pp_char_styles );
2113 free( psz_unicode );
2114 #if defined(HAVE_FRIBIDI)
2117 free( pi_karaoke_bar );
2120 /* New Color mode only works in YUVA rendering mode --
2121 * (RGB mode has palette constraints on it). We therefore
2122 * need to populate the legacy colour fields also.
2124 p_line->b_new_color_mode = true;
2125 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2126 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2127 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2128 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2129 p_line->p_next = NULL;
2131 i_pen_y += tmp_result.y;
2135 if( p_prev ) p_prev->p_next = p_line;
2136 else *pp_lines = p_line;
2139 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2140 p_style->i_font_color, p_style->b_underline,
2141 p_style->i_karaoke_bg_color,
2142 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2143 &tmp_result ) != VLC_SUCCESS )
2145 if( p_face ) FT_Done_Face( p_face );
2146 free( pp_char_styles );
2147 free( psz_unicode );
2148 #if defined(HAVE_FRIBIDI)
2151 free( pi_karaoke_bar );
2152 return VLC_EGENERIC;
2157 p_result->x = __MAX( p_result->x, tmp_result.x );
2158 p_result->y += tmp_result.y;
2163 if( *psz_unicode == '\n')
2167 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2169 *c_ptr = *(c_ptr+1);
2174 free( psz_unicode );
2175 if( p_face ) FT_Done_Face( p_face );
2179 free( pp_char_styles );
2180 #if defined(HAVE_FRIBIDI)
2185 p_result->x = __MAX( p_result->x, tmp_result.x );
2186 p_result->y += tmp_result.y;
2189 if( pi_karaoke_bar )
2192 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2194 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2196 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2200 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2202 /* 100% BG colour will render faster if we
2203 * instead make it 100% FG colour, so leave
2204 * the ratio alone and copy the value across
2206 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2210 if( pi_karaoke_bar[ i ] & 0x80 )
2212 /* Swap Left and Right sides over for Right aligned
2213 * language text (eg. Arabic, Hebrew)
2215 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2217 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2218 p_line->p_bg_rgb[ k ] = i_tmp;
2220 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2223 /* Jump over the '\n' at the line-end */
2226 free( pi_karaoke_bar );
2232 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2233 subpicture_region_t *p_region_in )
2235 int rv = VLC_SUCCESS;
2236 stream_t *p_sub = NULL;
2237 xml_t *p_xml = NULL;
2238 xml_reader_t *p_xml_reader = NULL;
2240 if( !p_region_in || !p_region_in->psz_html )
2241 return VLC_EGENERIC;
2243 /* Reset the default fontsize in case screen metrics have changed */
2244 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2246 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2247 (uint8_t *) p_region_in->psz_html,
2248 strlen( p_region_in->psz_html ),
2252 p_xml = xml_Create( p_filter );
2255 bool b_karaoke = false;
2257 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2260 /* Look for Root Node */
2261 if( xml_ReaderRead( p_xml_reader ) == 1 )
2263 char *psz_node = xml_ReaderName( p_xml_reader );
2265 if( !strcasecmp( "karaoke", psz_node ) )
2267 /* We're going to have to render the text a number
2268 * of times to show the progress marker on the text.
2270 var_SetBool( p_filter, "text-rerender", true );
2273 else if( !strcasecmp( "text", psz_node ) )
2279 /* Only text and karaoke tags are supported */
2280 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2281 xml_ReaderDelete( p_xml, p_xml_reader );
2282 p_xml_reader = NULL;
2294 uint32_t i_runs = 0;
2295 uint32_t i_k_runs = 0;
2296 uint32_t *pi_run_lengths = NULL;
2297 uint32_t *pi_k_run_lengths = NULL;
2298 uint32_t *pi_k_durations = NULL;
2299 ft_style_t **pp_styles = NULL;
2301 line_desc_t *p_lines = NULL;
2303 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2304 sizeof( uint32_t ) );
2309 rv = ProcessNodes( p_filter, p_xml_reader,
2310 p_region_in->p_style, psz_text, &i_len,
2311 &i_runs, &pi_run_lengths, &pp_styles,
2313 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2316 p_region_out->i_x = p_region_in->i_x;
2317 p_region_out->i_y = p_region_in->i_y;
2319 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2321 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2322 pi_run_lengths, pp_styles, &p_lines, &result,
2323 b_karaoke, i_k_runs, pi_k_run_lengths,
2327 for( k=0; k<i_runs; k++)
2328 DeleteStyle( pp_styles[k] );
2330 free( pi_run_lengths );
2333 /* Don't attempt to render text that couldn't be layed out
2336 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2338 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2340 Render( p_filter, p_region_out, p_lines,
2341 result.x, result.y );
2345 RenderYUVA( p_filter, p_region_out, p_lines,
2346 result.x, result.y );
2350 FreeLines( p_lines );
2352 xml_ReaderDelete( p_xml, p_xml_reader );
2354 xml_Delete( p_xml );
2356 stream_Delete( p_sub );
2362 static char* FontConfig_Select( FcConfig* priv, const char* family,
2363 bool b_bold, bool b_italic, int *i_idx )
2366 FcPattern *pat, *p_pat;
2370 pat = FcPatternCreate();
2371 if (!pat) return NULL;
2373 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2374 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2375 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2376 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2378 FcDefaultSubstitute( pat );
2380 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2382 FcPatternDestroy( pat );
2386 p_pat = FcFontMatch( priv, pat, &result );
2387 FcPatternDestroy( pat );
2388 if( !p_pat ) return NULL;
2390 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2391 || ( val_b != FcTrue ) )
2393 FcPatternDestroy( p_pat );
2396 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2401 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2403 FcPatternDestroy( p_pat );
2408 if( strcasecmp((const char*)val_s, family ) != 0 )
2409 msg_Warn( p_filter, "fontconfig: selected font family is not"
2410 "the requested one: '%s' != '%s'\n",
2411 (const char*)val_s, family );
2414 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2416 FcPatternDestroy( p_pat );
2420 FcPatternDestroy( p_pat );
2421 return strdup( (const char*)val_s );
2425 static void FreeLine( line_desc_t *p_line )
2428 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2430 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2432 free( p_line->pp_glyphs );
2433 free( p_line->p_glyph_pos );
2434 free( p_line->p_fg_rgb );
2435 free( p_line->p_bg_rgb );
2436 free( p_line->p_fg_bg_ratio );
2437 free( p_line->pi_underline_offset );
2438 free( p_line->pi_underline_thickness );
2442 static void FreeLines( line_desc_t *p_lines )
2444 line_desc_t *p_line, *p_next;
2446 if( !p_lines ) return;
2448 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2450 p_next = p_line->p_next;
2455 static line_desc_t *NewLine( int i_count )
2457 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2459 if( !p_line ) return NULL;
2460 p_line->i_height = 0;
2461 p_line->i_width = 0;
2462 p_line->p_next = NULL;
2464 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2465 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2466 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2467 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2468 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2469 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2470 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2471 if( ( p_line->pp_glyphs == NULL ) ||
2472 ( p_line->p_glyph_pos == NULL ) ||
2473 ( p_line->p_fg_rgb == NULL ) ||
2474 ( p_line->p_bg_rgb == NULL ) ||
2475 ( p_line->p_fg_bg_ratio == NULL ) ||
2476 ( p_line->pi_underline_offset == NULL ) ||
2477 ( p_line->pi_underline_thickness == NULL ) )
2479 free( p_line->pi_underline_thickness );
2480 free( p_line->pi_underline_offset );
2481 free( p_line->p_fg_rgb );
2482 free( p_line->p_bg_rgb );
2483 free( p_line->p_fg_bg_ratio );
2484 free( p_line->p_glyph_pos );
2485 free( p_line->pp_glyphs );
2489 p_line->pp_glyphs[0] = NULL;
2490 p_line->b_new_color_mode = false;
2495 static int GetFontSize( filter_t *p_filter )
2497 filter_sys_t *p_sys = p_filter->p_sys;
2501 if( p_sys->i_default_font_size )
2503 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2504 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2506 i_size = p_sys->i_default_font_size;
2510 var_Get( p_filter, "freetype-rel-fontsize", &val );
2513 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2514 p_filter->p_sys->i_display_height =
2515 p_filter->fmt_out.video.i_height;
2520 msg_Warn( p_filter, "invalid fontsize, using 12" );
2521 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2522 i_size = 12 * val.i_int / 1000;
2529 static int SetFontSize( filter_t *p_filter, int i_size )
2531 filter_sys_t *p_sys = p_filter->p_sys;
2535 i_size = GetFontSize( p_filter );
2537 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2540 p_sys->i_font_size = i_size;
2542 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2544 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2545 return VLC_EGENERIC;
2551 static void YUVFromRGB( uint32_t i_argb,
2552 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2554 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2555 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2556 int i_blue = ( i_argb & 0x000000ff );
2558 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2559 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2560 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2561 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2562 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2563 -585 * i_blue + 4096 + 1048576) >> 13, 240);