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>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
52 #define FT_MulFix(v, s) (((v)*(s))>>16)
55 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
56 #define FC_DEFAULT_FONT "Lucida Grande"
57 #elif defined( SYS_BEOS )
58 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
59 #define FC_DEFAULT_FONT "Swiss"
60 #elif defined( WIN32 )
61 #define DEFAULT_FONT "" /* Default font found at run-time */
62 #define FC_DEFAULT_FONT "Arial"
64 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
65 #define FC_DEFAULT_FONT "Serif Bold"
68 #if defined(HAVE_FRIBIDI)
69 #include <fribidi/fribidi.h>
72 #ifdef HAVE_FONTCONFIG
73 #include <fontconfig/fontconfig.h>
78 #include "text_renderer.h"
80 /*****************************************************************************
82 *****************************************************************************/
83 static int Create ( vlc_object_t * );
84 static void Destroy( vlc_object_t * );
86 #define FONT_TEXT N_("Font")
87 #define FONT_LONGTEXT N_("Filename for the font you want to use")
88 #define FONTSIZE_TEXT N_("Font size in pixels")
89 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
90 "that will be rendered on the video. " \
91 "If set to something different than 0 this option will override the " \
92 "relative font size." )
93 #define OPACITY_TEXT N_("Opacity")
94 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
95 "text that will be rendered on the video. 0 = transparent, " \
96 "255 = totally opaque. " )
97 #define COLOR_TEXT N_("Text default color")
98 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
99 "the video. This must be an hexadecimal (like HTML colors). The first two "\
100 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
101 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
102 #define FONTSIZER_TEXT N_("Relative font size")
103 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
104 "fonts that will be rendered on the video. If absolute font size is set, "\
105 "relative size will be overriden." )
107 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
108 static const char *const ppsz_sizes_text[] = {
109 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
110 #define YUVP_TEXT N_("Use YUVP renderer")
111 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
112 "This option is only needed if you want to encode into DVB subtitles" )
113 #define EFFECT_TEXT N_("Font Effect")
114 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
115 "text to improve its readability." )
117 #define EFFECT_BACKGROUND 1
118 #define EFFECT_OUTLINE 2
119 #define EFFECT_OUTLINE_FAT 3
121 static int const pi_effects[] = { 1, 2, 3 };
122 static const char *const ppsz_effects_text[] = {
123 N_("Background"),N_("Outline"), N_("Fat Outline") };
124 static const int pi_color_values[] = {
125 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
126 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
127 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
129 static const char *const ppsz_color_descriptions[] = {
130 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
131 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
132 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
135 set_shortname( N_("Text renderer"));
136 set_description( N_("Freetype2 font renderer") );
137 set_category( CAT_VIDEO );
138 set_subcategory( SUBCAT_VIDEO_SUBPIC );
140 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
143 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
144 FONTSIZE_LONGTEXT, true );
146 /* opacity valid on 0..255, with default 255 = fully opaque */
147 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
148 OPACITY_TEXT, OPACITY_LONGTEXT, true );
150 /* hook to the color values list, with default 0x00ffffff = white */
151 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
152 COLOR_LONGTEXT, false );
153 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
155 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
156 FONTSIZER_LONGTEXT, false );
157 change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
158 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
159 EFFECT_LONGTEXT, false );
160 change_integer_list( pi_effects, ppsz_effects_text, NULL );
162 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
163 YUVP_LONGTEXT, true );
164 set_capability( "text renderer", 100 );
165 add_shortcut( "text" );
166 set_callbacks( Create, Destroy );
171 /*****************************************************************************
173 *****************************************************************************/
175 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
176 static int RenderText( filter_t *, subpicture_region_t *,
177 subpicture_region_t * );
178 #ifdef HAVE_FONTCONFIG
179 static int RenderHtml( filter_t *, subpicture_region_t *,
180 subpicture_region_t * );
181 static char *FontConfig_Select( FcConfig *, const char *,
186 static int LoadFontsFromAttachments( filter_t *p_filter );
188 static int GetFontSize( filter_t *p_filter );
189 static int SetFontSize( filter_t *, int );
190 static void YUVFromRGB( uint32_t i_argb,
191 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
193 typedef struct line_desc_t line_desc_t;
196 /** NULL-terminated list of glyphs making the string */
197 FT_BitmapGlyph *pp_glyphs;
198 /** list of relative positions for the glyphs */
199 FT_Vector *p_glyph_pos;
200 /** list of RGB information for styled text
201 * -- if the rendering mode supports it (RenderYUVA) and
202 * b_new_color_mode is set, then it becomes possible to
203 * have multicoloured text within the subtitles. */
206 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
207 bool b_new_color_mode;
208 /** underline information -- only supplied if text should be underlined */
209 uint16_t *pi_underline_offset;
210 uint16_t *pi_underline_thickness;
214 int i_red, i_green, i_blue;
219 static line_desc_t *NewLine( int );
224 uint32_t i_font_color; /* ARGB */
225 uint32_t i_karaoke_bg_color; /* ARGB */
232 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
233 static void FreeLines( line_desc_t * );
234 static void FreeLine( line_desc_t * );
236 #ifdef HAVE_FONTCONFIG
237 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock );
238 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
239 static void* FontBuilderThread( vlc_object_t *p_this);
240 static void FontBuilderDestructor( vlc_object_t *p_this );
241 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
242 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
246 /*****************************************************************************
247 * filter_sys_t: freetype local data
248 *****************************************************************************
249 * This structure is part of the video output thread descriptor.
250 * It describes the freetype specific properties of an output thread.
251 *****************************************************************************/
254 FT_Library p_library; /* handle to library */
255 FT_Face p_face; /* handle to face object */
257 uint8_t i_font_opacity;
262 int i_default_font_size;
263 int i_display_height;
264 #ifdef HAVE_FONTCONFIG
265 vlc_mutex_t *p_fontconfig_lock;
266 bool b_fontconfig_ok;
267 FcConfig *p_fontconfig;
270 input_attachment_t **pp_font_attachments;
271 int i_font_attachments;
273 vlc_object_t *p_fontbuilder;
276 /*****************************************************************************
277 * Create: allocates osd-text video thread output method
278 *****************************************************************************
279 * This function allocates and initializes a Clone vout method.
280 *****************************************************************************/
281 static int Create( vlc_object_t *p_this )
283 filter_t *p_filter = (filter_t *)p_this;
285 char *psz_fontfile = NULL;
289 /* Allocate structure */
290 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
294 p_sys->p_library = 0;
295 p_sys->i_font_size = 0;
296 p_sys->i_display_height = 0;
298 var_Create( p_filter, "freetype-font",
299 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
300 var_Create( p_filter, "freetype-fontsize",
301 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
302 var_Create( p_filter, "freetype-rel-fontsize",
303 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
304 var_Create( p_filter, "freetype-opacity",
305 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
306 var_Create( p_filter, "freetype-effect",
307 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
308 var_Get( p_filter, "freetype-opacity", &val );
309 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
310 var_Create( p_filter, "freetype-color",
311 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
312 var_Get( p_filter, "freetype-color", &val );
313 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
314 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
316 /* Look what method was requested */
317 var_Get( p_filter, "freetype-font", &val );
318 psz_fontfile = val.psz_string;
319 if( !psz_fontfile || !*psz_fontfile )
321 free( psz_fontfile );
322 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
326 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
327 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
328 #elif defined(__APPLE__)
329 strcpy( psz_fontfile, DEFAULT_FONT );
331 msg_Err( p_filter, "user didn't specify a font" );
336 i_error = FT_Init_FreeType( &p_sys->p_library );
339 msg_Err( p_filter, "couldn't initialize freetype" );
342 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
344 if( i_error == FT_Err_Unknown_File_Format )
346 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
351 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
355 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
358 msg_Err( p_filter, "font has no unicode translation table" );
362 #ifdef HAVE_FONTCONFIG
363 p_sys->b_fontconfig_ok = false;
364 p_sys->p_fontconfig = NULL;
365 p_sys->p_fontbuilder = FontBuilderAttach( p_filter, &p_sys->p_fontconfig_lock );
368 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
370 var_Get( p_filter, "freetype-fontsize", &val );
371 p_sys->i_default_font_size = val.i_int;
372 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
374 free( psz_fontfile );
376 p_sys->pp_font_attachments = NULL;
377 p_sys->i_font_attachments = 0;
379 p_filter->pf_render_text = RenderText;
380 #ifdef HAVE_FONTCONFIG
381 p_filter->pf_render_html = RenderHtml;
383 p_filter->pf_render_html = NULL;
386 LoadFontsFromAttachments( p_filter );
391 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
392 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
393 free( psz_fontfile );
398 /*****************************************************************************
399 * Destroy: destroy Clone video thread output method
400 *****************************************************************************
401 * Clean up all data and library connections
402 *****************************************************************************/
403 static void Destroy( vlc_object_t *p_this )
405 filter_t *p_filter = (filter_t *)p_this;
406 filter_sys_t *p_sys = p_filter->p_sys;
408 if( p_sys->pp_font_attachments )
412 for( k = 0; k < p_sys->i_font_attachments; k++ )
413 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
415 free( p_sys->pp_font_attachments );
418 #ifdef HAVE_FONTCONFIG
419 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
422 /* FcFini asserts calling the subfunction FcCacheFini()
423 * even if no other library functions have been made since FcInit(),
424 * so don't call it. */
426 FT_Done_Face( p_sys->p_face );
427 FT_Done_FreeType( p_sys->p_library );
431 #ifdef HAVE_FONTCONFIG
432 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock )
434 /* Check for an existing Fontbuilder thread */
435 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
436 vlc_object_t *p_fontbuilder =
437 vlc_object_find_name( p_filter->p_libvlc,
438 "fontlist builder", FIND_CHILD );
442 /* Create the FontBuilderThread thread as a child of a top-level
443 * object, so that it can survive the destruction of the
444 * freetype object - the fontlist only needs to be built once,
445 * and calling the fontbuild a second time while the first is
446 * still in progress can cause thread instabilities.
448 * XXX The fontbuilder will be destroy as soon as it is unused.
451 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
452 sizeof(vlc_object_t) );
455 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
456 p_fontbuilder->p_private = NULL;
457 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
459 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
461 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
462 var_SetBool( p_fontbuilder, "build-done", false );
464 if( vlc_thread_create( p_fontbuilder,
467 VLC_THREAD_PRIORITY_LOW,
470 msg_Warn( p_filter, "fontconfig database builder thread can't "
471 "be launched. Font styling support will be limited." );
477 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
478 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
480 vlc_mutex_unlock( p_lock );
482 return p_fontbuilder;
484 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
486 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
489 const bool b_alive = vlc_object_alive( p_fontbuilder );
491 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
493 /* We wait for the thread on the first FontBuilderDetach */
496 vlc_object_kill( p_fontbuilder );
497 vlc_mutex_unlock( lock );
499 /* We need to unlock otherwise we may not join (the thread waiting
500 * for the lock). It is safe to unlock as no one else will try a
501 * join and we have a reference on the object) */
502 vlc_thread_join( p_fontbuilder );
504 vlc_mutex_lock( lock );
506 vlc_object_release( p_fontbuilder );
508 vlc_mutex_unlock( lock );
510 static void* FontBuilderThread( vlc_object_t *p_this )
512 FcConfig *p_fontconfig = FcInitLoadConfig();
514 vlc_thread_ready( p_this );
519 int canc = vlc_savecancel ();
521 //msg_Dbg( p_this, "Building font database..." );
522 msg_Dbg( p_this, "Building font database..." );
524 if(! FcConfigBuildFonts( p_fontconfig ))
526 /* Don't destroy the fontconfig object - we won't be able to do
527 * italics or bold or change the font face, but we will still
528 * be able to do underline and change the font size.
530 msg_Err( p_this, "fontconfig database can't be built. "
531 "Font styling won't be available" );
535 msg_Dbg( p_this, "Finished building font database." );
536 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
538 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
539 p_this->p_private = p_fontconfig;
540 vlc_mutex_unlock( p_lock );
542 var_SetBool( p_this, "build-done", true );
543 vlc_restorecancel (canc);
547 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
549 filter_sys_t *p_sys = p_filter->p_sys;
551 p_sys->p_fontconfig = p_fontbuilder->p_private;
552 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
554 static void FontBuilderDestructor( vlc_object_t *p_this )
556 FcConfig *p_fontconfig = p_this->p_private;
559 FcConfigDestroy( p_fontconfig );
561 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
562 vlc_value_t oldval, vlc_value_t newval, void *param )
564 filter_t *p_filter = param;
568 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
570 FontBuilderGetFcConfig( p_filter, p_this );
572 vlc_mutex_unlock( p_lock );
581 /*****************************************************************************
582 * Make any TTF/OTF fonts present in the attachments of the media file
583 * and store them for later use by the FreeType Engine
584 *****************************************************************************/
585 static int LoadFontsFromAttachments( filter_t *p_filter )
587 filter_sys_t *p_sys = p_filter->p_sys;
588 input_thread_t *p_input;
589 input_attachment_t **pp_attachments;
590 int i_attachments_cnt;
592 int rv = VLC_SUCCESS;
594 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
598 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
600 vlc_object_release(p_input);
604 p_sys->i_font_attachments = 0;
605 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
606 if(! p_sys->pp_font_attachments )
609 for( k = 0; k < i_attachments_cnt; k++ )
611 input_attachment_t *p_attach = pp_attachments[k];
613 if( p_sys->pp_font_attachments )
615 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
616 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
617 ( p_attach->i_data > 0 ) &&
618 ( p_attach->p_data != NULL ) )
620 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
624 vlc_input_attachment_Delete( p_attach );
629 vlc_input_attachment_Delete( p_attach );
632 free( pp_attachments );
634 vlc_object_release(p_input);
639 /*****************************************************************************
640 * Render: place string in picture
641 *****************************************************************************
642 * This function merges the previously rendered freetype glyphs into a picture
643 *****************************************************************************/
644 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
645 line_desc_t *p_line, int i_width, int i_height )
647 static const uint8_t pi_gamma[16] =
648 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
649 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
653 int i, x, y, i_pitch;
654 uint8_t i_y; /* YUV values, derived from incoming RGB */
657 /* Create a new subpicture region */
658 memset( &fmt, 0, sizeof(video_format_t) );
659 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
661 fmt.i_width = fmt.i_visible_width = i_width + 4;
662 fmt.i_height = fmt.i_visible_height = i_height + 4;
663 if( p_region->fmt.i_visible_width > 0 )
664 fmt.i_visible_width = p_region->fmt.i_visible_width;
665 if( p_region->fmt.i_visible_height > 0 )
666 fmt.i_visible_height = p_region->fmt.i_visible_height;
667 fmt.i_x_offset = fmt.i_y_offset = 0;
669 assert( !p_region->p_picture );
670 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
671 if( !p_region->p_picture )
675 /* Calculate text color components */
676 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
677 25 * p_line->i_blue + 128) >> 8) + 16;
678 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
679 112 * p_line->i_blue + 128) >> 8) + 128;
680 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
681 18 * p_line->i_blue + 128) >> 8) + 128;
684 fmt.p_palette->i_entries = 16;
685 for( i = 0; i < 8; i++ )
687 fmt.p_palette->palette[i][0] = 0;
688 fmt.p_palette->palette[i][1] = 0x80;
689 fmt.p_palette->palette[i][2] = 0x80;
690 fmt.p_palette->palette[i][3] = pi_gamma[i];
691 fmt.p_palette->palette[i][3] =
692 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
694 for( i = 8; i < fmt.p_palette->i_entries; i++ )
696 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
697 fmt.p_palette->palette[i][1] = i_u;
698 fmt.p_palette->palette[i][2] = i_v;
699 fmt.p_palette->palette[i][3] = pi_gamma[i];
700 fmt.p_palette->palette[i][3] =
701 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
704 p_dst = p_region->p_picture->Y_PIXELS;
705 i_pitch = p_region->p_picture->Y_PITCH;
707 /* Initialize the region pixels */
708 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
710 for( ; p_line != NULL; p_line = p_line->p_next )
712 int i_glyph_tmax = 0;
713 int i_bitmap_offset, i_offset, i_align_offset = 0;
714 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
716 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
717 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
720 if( p_line->i_width < i_width )
722 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
724 i_align_offset = i_width - p_line->i_width;
726 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
728 i_align_offset = ( i_width - p_line->i_width ) / 2;
732 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
734 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
736 i_offset = ( p_line->p_glyph_pos[ i ].y +
737 i_glyph_tmax - p_glyph->top + 2 ) *
738 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
741 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
743 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
745 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
747 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
754 /* Outlining (find something better than nearest neighbour filtering ?) */
757 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
758 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
759 uint8_t left, current;
761 for( y = 1; y < (int)fmt.i_height - 1; y++ )
763 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
764 p_dst += p_region->p_picture->Y_PITCH;
767 for( x = 1; x < (int)fmt.i_width - 1; x++ )
770 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
771 p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
775 memset( p_top, 0, fmt.i_width );
781 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
782 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
783 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
784 int i_glyph_tmax, int i_align_offset,
785 uint8_t i_y, uint8_t i_u, uint8_t i_v,
786 subpicture_region_t *p_region)
790 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
792 p_dst_y = p_region->p_picture->Y_PIXELS;
793 p_dst_u = p_region->p_picture->U_PIXELS;
794 p_dst_v = p_region->p_picture->V_PIXELS;
795 p_dst_a = p_region->p_picture->A_PIXELS;
796 i_pitch = p_region->p_picture->A_PITCH;
798 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
799 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
801 for( y = 0; y < i_line_thickness; y++ )
803 int i_extra = p_this_glyph->bitmap.width;
807 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
808 (p_this_glyph_pos->x + p_this_glyph->left);
810 for( x = 0; x < i_extra; x++ )
814 /* break the underline around the tails of any glyphs which cross it */
815 for( z = x - i_line_thickness;
816 z < x + i_line_thickness && b_ok;
819 if( p_next_glyph && ( z >= i_extra ) )
821 int i_row = i_line_offset + p_next_glyph->top + y;
823 if( ( p_next_glyph->bitmap.rows > i_row ) &&
824 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
829 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
831 int i_row = i_line_offset + p_this_glyph->top + y;
833 if( ( p_this_glyph->bitmap.rows > i_row ) &&
834 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
843 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
844 p_dst_u[i_offset+x] = i_u;
845 p_dst_v[i_offset+x] = i_v;
846 p_dst_a[i_offset+x] = 255;
853 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
855 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
856 int i_pitch = p_region->p_picture->A_PITCH;
859 for( ; p_line != NULL; p_line = p_line->p_next )
861 int i_glyph_tmax=0, i = 0;
862 int i_bitmap_offset, i_offset, i_align_offset = 0;
863 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
865 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
866 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
869 if( p_line->i_width < i_width )
871 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
873 i_align_offset = i_width - p_line->i_width;
875 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
877 i_align_offset = ( i_width - p_line->i_width ) / 2;
881 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
883 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
885 i_offset = ( p_line->p_glyph_pos[ i ].y +
886 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
887 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
888 i_align_offset +xoffset;
890 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
892 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
894 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
895 if( p_dst[i_offset+x] <
896 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
898 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
907 /*****************************************************************************
908 * Render: place string in picture
909 *****************************************************************************
910 * This function merges the previously rendered freetype glyphs into a picture
911 *****************************************************************************/
912 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
913 line_desc_t *p_line, int i_width, int i_height )
915 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
917 int i, x, y, i_pitch, i_alpha;
918 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
920 if( i_width == 0 || i_height == 0 )
923 /* Create a new subpicture region */
924 memset( &fmt, 0, sizeof(video_format_t) );
925 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
927 fmt.i_width = fmt.i_visible_width = i_width + 6;
928 fmt.i_height = fmt.i_visible_height = i_height + 6;
929 if( p_region->fmt.i_visible_width > 0 )
930 fmt.i_visible_width = p_region->fmt.i_visible_width;
931 if( p_region->fmt.i_visible_height > 0 )
932 fmt.i_visible_height = p_region->fmt.i_visible_height;
933 fmt.i_x_offset = fmt.i_y_offset = 0;
935 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
936 if( !p_region->p_picture )
940 /* Calculate text color components */
941 YUVFromRGB( (p_line->i_red << 16) |
942 (p_line->i_green << 8) |
945 i_alpha = p_line->i_alpha;
947 p_dst_y = p_region->p_picture->Y_PIXELS;
948 p_dst_u = p_region->p_picture->U_PIXELS;
949 p_dst_v = p_region->p_picture->V_PIXELS;
950 p_dst_a = p_region->p_picture->A_PIXELS;
951 i_pitch = p_region->p_picture->A_PITCH;
953 /* Initialize the region pixels */
954 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
956 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
957 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
958 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
959 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
963 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
964 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
965 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
966 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
968 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
969 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
971 DrawBlack( p_line, i_width, p_region, 0, 0);
972 DrawBlack( p_line, i_width, p_region, -1, 0);
973 DrawBlack( p_line, i_width, p_region, 0, -1);
974 DrawBlack( p_line, i_width, p_region, 1, 0);
975 DrawBlack( p_line, i_width, p_region, 0, 1);
978 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
980 DrawBlack( p_line, i_width, p_region, -1, -1);
981 DrawBlack( p_line, i_width, p_region, -1, 1);
982 DrawBlack( p_line, i_width, p_region, 1, -1);
983 DrawBlack( p_line, i_width, p_region, 1, 1);
985 DrawBlack( p_line, i_width, p_region, -2, 0);
986 DrawBlack( p_line, i_width, p_region, 0, -2);
987 DrawBlack( p_line, i_width, p_region, 2, 0);
988 DrawBlack( p_line, i_width, p_region, 0, 2);
990 DrawBlack( p_line, i_width, p_region, -2, -2);
991 DrawBlack( p_line, i_width, p_region, -2, 2);
992 DrawBlack( p_line, i_width, p_region, 2, -2);
993 DrawBlack( p_line, i_width, p_region, 2, 2);
995 DrawBlack( p_line, i_width, p_region, -3, 0);
996 DrawBlack( p_line, i_width, p_region, 0, -3);
997 DrawBlack( p_line, i_width, p_region, 3, 0);
998 DrawBlack( p_line, i_width, p_region, 0, 3);
1001 for( ; p_line != NULL; p_line = p_line->p_next )
1003 int i_glyph_tmax = 0;
1004 int i_bitmap_offset, i_offset, i_align_offset = 0;
1005 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1007 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1008 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1011 if( p_line->i_width < i_width )
1013 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1015 i_align_offset = i_width - p_line->i_width;
1017 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1019 i_align_offset = ( i_width - p_line->i_width ) / 2;
1023 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1025 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1027 i_offset = ( p_line->p_glyph_pos[ i ].y +
1028 i_glyph_tmax - p_glyph->top + 3 ) *
1029 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1032 if( p_line->b_new_color_mode )
1034 /* Every glyph can (and in fact must) have its own color */
1035 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1038 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1040 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1042 uint8_t i_y_local = i_y;
1043 uint8_t i_u_local = i_u;
1044 uint8_t i_v_local = i_v;
1046 if( p_line->p_fg_bg_ratio != 0x00 )
1048 int i_split = p_glyph->bitmap.width *
1049 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1053 YUVFromRGB( p_line->p_bg_rgb[ i ],
1054 &i_y_local, &i_u_local, &i_v_local );
1058 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1060 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1061 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1063 p_dst_u[i_offset+x] = i_u;
1064 p_dst_v[i_offset+x] = i_v;
1066 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1067 p_dst_a[i_offset+x] = 0xff;
1070 i_offset += i_pitch;
1073 if( p_line->pi_underline_thickness[ i ] )
1075 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1076 p_line->pi_underline_offset[ i ],
1077 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1078 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1079 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1080 i_glyph_tmax, i_align_offset,
1087 /* Apply the alpha setting */
1088 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1089 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1095 * This function renders a text subpicture region into another one.
1096 * It also calculates the size needed for this string, and renders the
1097 * needed glyphs into memory. It is used as pf_add_string callback in
1098 * the vout method by this module
1100 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1101 subpicture_region_t *p_region_in )
1103 filter_sys_t *p_sys = p_filter->p_sys;
1104 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1105 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1106 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1107 int i_string_length;
1109 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1110 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1120 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1121 psz_string = p_region_in->psz_text;
1122 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1124 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1125 i_scale = val.i_int;
1127 if( p_region_in->p_style )
1129 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1130 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1131 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1135 i_font_color = p_sys->i_font_color;
1136 i_font_alpha = 255 - p_sys->i_font_opacity;
1137 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1140 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1141 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1142 SetFontSize( p_filter, i_font_size );
1144 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1145 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1146 i_blue = i_font_color & 0x000000FF;
1148 result.x = result.y = 0;
1149 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1151 psz_unicode = psz_unicode_orig =
1152 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1153 if( psz_unicode == NULL )
1155 #if defined(WORDS_BIGENDIAN)
1156 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1158 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1160 if( iconv_handle == (vlc_iconv_t)-1 )
1162 msg_Warn( p_filter, "unable to do conversion" );
1168 const char *p_in_buffer = psz_string;
1169 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1170 i_in_bytes = strlen( psz_string );
1171 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1172 i_out_bytes_left = i_out_bytes;
1173 p_out_buffer = (char *)psz_unicode;
1174 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1176 &p_out_buffer, &i_out_bytes_left );
1178 vlc_iconv_close( iconv_handle );
1182 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1183 "bytes left %u", (unsigned)i_in_bytes );
1186 *(uint32_t*)p_out_buffer = 0;
1187 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1190 #if defined(HAVE_FRIBIDI)
1192 uint32_t *p_fribidi_string;
1193 int32_t start_pos, pos = 0;
1195 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1196 if( !p_fribidi_string )
1199 /* Do bidi conversion line-by-line */
1200 while( pos < i_string_length )
1202 while( pos < i_string_length )
1204 i_char = psz_unicode[pos];
1205 if (i_char != '\r' && i_char != '\n')
1207 p_fribidi_string[pos] = i_char;
1211 while( pos < i_string_length )
1213 i_char = psz_unicode[pos];
1214 if (i_char == '\r' || i_char == '\n')
1218 if (pos > start_pos)
1220 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1221 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1224 (FriBidiChar*)p_fribidi_string + start_pos,
1229 free( psz_unicode_orig );
1230 psz_unicode = psz_unicode_orig = p_fribidi_string;
1231 p_fribidi_string[ i_string_length ] = 0;
1235 /* Calculate relative glyph positions and a bounding box for the
1237 if( !(p_line = NewLine( strlen( psz_string ))) )
1240 i_pen_x = i_pen_y = 0;
1242 psz_line_start = psz_unicode;
1244 #define face p_sys->p_face
1245 #define glyph face->glyph
1247 while( *psz_unicode )
1249 i_char = *psz_unicode++;
1250 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1255 if( i_char == '\n' )
1257 psz_line_start = psz_unicode;
1258 if( !(p_next = NewLine( strlen( psz_string ))) )
1260 p_line->p_next = p_next;
1261 p_line->i_width = line.xMax;
1262 p_line->i_height = face->size->metrics.height >> 6;
1263 p_line->pp_glyphs[ i ] = NULL;
1264 p_line->i_alpha = i_font_alpha;
1265 p_line->i_red = i_red;
1266 p_line->i_green = i_green;
1267 p_line->i_blue = i_blue;
1270 result.x = __MAX( result.x, line.xMax );
1271 result.y += face->size->metrics.height >> 6;
1274 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1275 i_pen_y += face->size->metrics.height >> 6;
1277 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1282 i_glyph_index = FT_Get_Char_Index( face, i_char );
1283 if( p_sys->i_use_kerning && i_glyph_index
1287 FT_Get_Kerning( face, i_previous, i_glyph_index,
1288 ft_kerning_default, &delta );
1289 i_pen_x += delta.x >> 6;
1292 p_line->p_glyph_pos[ i ].x = i_pen_x;
1293 p_line->p_glyph_pos[ i ].y = i_pen_y;
1294 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1297 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1301 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1304 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1308 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1309 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1312 FT_Done_Glyph( tmp_glyph );
1315 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1318 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1319 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1320 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1322 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1323 p_line->pp_glyphs[ i ] = NULL;
1325 p_line = NewLine( strlen( psz_string ));
1326 if( p_prev ) p_prev->p_next = p_line;
1327 else p_lines = p_line;
1329 uint32_t *psz_unicode_saved = psz_unicode;
1330 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1334 if( psz_unicode == psz_line_start )
1335 { /* try harder to break that line */
1336 psz_unicode = psz_unicode_saved;
1337 while( psz_unicode > psz_line_start &&
1338 *psz_unicode != '_' && *psz_unicode != '/' &&
1339 *psz_unicode != '\\' && *psz_unicode != '.' )
1344 if( psz_unicode == psz_line_start )
1346 msg_Warn( p_filter, "unbreakable string" );
1351 *psz_unicode = '\n';
1353 psz_unicode = psz_line_start;
1356 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1359 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1360 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1362 i_previous = i_glyph_index;
1363 i_pen_x += glyph->advance.x >> 6;
1367 p_line->i_width = line.xMax;
1368 p_line->i_height = face->size->metrics.height >> 6;
1369 p_line->pp_glyphs[ i ] = NULL;
1370 p_line->i_alpha = i_font_alpha;
1371 p_line->i_red = i_red;
1372 p_line->i_green = i_green;
1373 p_line->i_blue = i_blue;
1374 result.x = __MAX( result.x, line.xMax );
1375 result.y += line.yMax - line.yMin;
1380 p_region_out->i_x = p_region_in->i_x;
1381 p_region_out->i_y = p_region_in->i_y;
1383 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1384 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1386 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1388 free( psz_unicode_orig );
1389 FreeLines( p_lines );
1393 free( psz_unicode_orig );
1394 FreeLines( p_lines );
1395 return VLC_EGENERIC;
1398 #ifdef HAVE_FONTCONFIG
1399 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1400 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1401 bool b_italic, bool b_uline )
1403 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1407 p_style->i_font_size = i_font_size;
1408 p_style->i_font_color = i_font_color;
1409 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1410 p_style->b_italic = b_italic;
1411 p_style->b_bold = b_bold;
1412 p_style->b_underline = b_uline;
1414 p_style->psz_fontname = strdup( psz_fontname );
1419 static void DeleteStyle( ft_style_t *p_style )
1423 free( p_style->psz_fontname );
1428 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1435 if(( s1->i_font_size == s2->i_font_size ) &&
1436 ( s1->i_font_color == s2->i_font_color ) &&
1437 ( s1->b_italic == s2->b_italic ) &&
1438 ( s1->b_bold == s2->b_bold ) &&
1439 ( s1->b_underline == s2->b_underline ) &&
1440 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1447 static void IconvText( filter_t *p_filter, const char *psz_string,
1448 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1450 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1452 /* If memory hasn't been allocated for our output string, allocate it here
1453 * - the calling function must now be responsible for freeing it.
1455 if( !*ppsz_unicode )
1456 *ppsz_unicode = (uint32_t *)
1457 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1459 /* We don't need to handle a NULL pointer in *ppsz_unicode
1460 * if we are instead testing for a non NULL value like we are here */
1464 #if defined(WORDS_BIGENDIAN)
1465 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1467 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1469 if( iconv_handle != (vlc_iconv_t)-1 )
1471 char *p_in_buffer, *p_out_buffer;
1472 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1473 i_in_bytes = strlen( psz_string );
1474 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1475 i_out_bytes_left = i_out_bytes;
1476 p_in_buffer = (char *) psz_string;
1477 p_out_buffer = (char *) *ppsz_unicode;
1478 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1479 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1481 vlc_iconv_close( iconv_handle );
1485 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1486 "bytes left %u", (unsigned)i_in_bytes );
1490 *(uint32_t*)p_out_buffer = 0;
1492 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1497 msg_Warn( p_filter, "unable to do conversion" );
1502 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1503 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1506 ft_style_t *p_style = NULL;
1508 char *psz_fontname = NULL;
1509 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1510 uint32_t i_karaoke_bg_color = i_font_color;
1511 int i_font_size = p_sys->i_font_size;
1513 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1514 &i_font_color, &i_karaoke_bg_color ))
1516 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1517 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1522 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1523 bool b_uline, int i_karaoke_bgcolor,
1524 line_desc_t *p_line, uint32_t *psz_unicode,
1525 int *pi_pen_x, int i_pen_y, int *pi_start,
1526 FT_Vector *p_result )
1531 bool b_first_on_line = true;
1534 int i_pen_x_start = *pi_pen_x;
1536 uint32_t *psz_unicode_start = psz_unicode;
1538 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1540 /* Account for part of line already in position */
1541 for( i=0; i<*pi_start; i++ )
1545 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1546 ft_glyph_bbox_pixels, &glyph_size );
1548 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1549 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1550 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1551 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1557 b_first_on_line = false;
1559 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1565 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1566 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1570 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1571 ft_kerning_default, &delta );
1572 *pi_pen_x += delta.x >> 6;
1574 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1575 p_line->p_glyph_pos[ i ].y = i_pen_y;
1577 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1581 "unable to render text FT_Load_Glyph returned %d", i_error );
1582 p_line->pp_glyphs[ i ] = NULL;
1583 return VLC_EGENERIC;
1585 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1589 "unable to render text FT_Get_Glyph returned %d", i_error );
1590 p_line->pp_glyphs[ i ] = NULL;
1591 return VLC_EGENERIC;
1593 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1594 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1597 FT_Done_Glyph( tmp_glyph );
1602 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1603 p_face->size->metrics.y_scale));
1604 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1605 p_face->size->metrics.y_scale));
1607 p_line->pi_underline_offset[ i ] =
1608 ( aOffset < 0 ) ? -aOffset : aOffset;
1609 p_line->pi_underline_thickness[ i ] =
1610 ( aSize < 0 ) ? -aSize : aSize;
1612 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1613 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1614 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1615 p_line->p_fg_bg_ratio[ i ] = 0x00;
1617 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1618 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1619 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1621 for( ; i >= *pi_start; i-- )
1622 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1625 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1629 if( psz_unicode == psz_unicode_start )
1631 if( b_first_on_line )
1633 msg_Warn( p_filter, "unbreakable string" );
1634 p_line->pp_glyphs[ i ] = NULL;
1635 return VLC_EGENERIC;
1637 *pi_pen_x = i_pen_x_start;
1639 p_line->i_width = line.xMax;
1640 p_line->i_height = __MAX( p_line->i_height,
1641 p_face->size->metrics.height >> 6 );
1642 p_line->pp_glyphs[ i ] = NULL;
1644 p_result->x = __MAX( p_result->x, line.xMax );
1645 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1646 i_yMax - i_yMin ) );
1651 *psz_unicode = '\n';
1653 psz_unicode = psz_unicode_start;
1654 *pi_pen_x = i_pen_x_start;
1662 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1663 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1665 i_previous = i_glyph_index;
1666 *pi_pen_x += p_face->glyph->advance.x >> 6;
1669 p_line->i_width = line.xMax;
1670 p_line->i_height = __MAX( p_line->i_height,
1671 p_face->size->metrics.height >> 6 );
1672 p_line->pp_glyphs[ i ] = NULL;
1674 p_result->x = __MAX( p_result->x, line.xMax );
1675 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1676 line.yMax - line.yMin ) );
1680 /* Get rid of any text processed - if necessary repositioning
1681 * at the start of a new line of text
1685 *psz_unicode_start = '\0';
1687 else if( psz_unicode > psz_unicode_start )
1689 for( i=0; psz_unicode[ i ]; i++ )
1690 psz_unicode_start[ i ] = psz_unicode[ i ];
1691 psz_unicode_start[ i ] = '\0';
1697 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1698 uint32_t **psz_text_out, uint32_t *pi_runs,
1699 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1700 ft_style_t *p_style )
1702 uint32_t i_string_length = 0;
1704 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1705 *psz_text_out += i_string_length;
1707 if( ppp_styles && ppi_run_lengths )
1713 *ppp_styles = (ft_style_t **)
1714 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1716 else if( *pi_runs == 1 )
1718 *ppp_styles = (ft_style_t **)
1719 malloc( *pi_runs * sizeof( ft_style_t * ) );
1722 /* We have just malloc'ed this memory successfully -
1723 * *pi_runs HAS to be within the memory area of *ppp_styles */
1726 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1730 if( *ppi_run_lengths )
1732 *ppi_run_lengths = (uint32_t *)
1733 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1735 else if( *pi_runs == 1 )
1737 *ppi_run_lengths = (uint32_t *)
1738 malloc( *pi_runs * sizeof( uint32_t ) );
1741 /* same remarks here */
1742 if( *ppi_run_lengths )
1744 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1747 /* If we couldn't use the p_style argument due to memory allocation
1748 * problems above, release it here.
1750 if( p_style ) DeleteStyle( p_style );
1753 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1754 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1756 /* Karaoke tags _PRECEDE_ the text they specify a duration
1757 * for, therefore we are working out the length for the
1758 * previous tag, and first time through we have nothing
1760 if( pi_k_run_lengths )
1765 /* Work out how many characters are presently in the string
1767 for( i = 0; i < i_runs; i++ )
1768 i_chars += pi_run_lengths[ i ];
1770 /* Subtract away those we've already allocated to other
1773 for( i = 0; i < i_k_runs; i++ )
1774 i_chars -= pi_k_run_lengths[ i ];
1776 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1780 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1781 uint32_t **ppi_k_run_lengths,
1782 uint32_t **ppi_k_durations )
1784 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1786 char *psz_name = xml_ReaderName( p_xml_reader );
1787 char *psz_value = xml_ReaderValue( p_xml_reader );
1789 if( psz_name && psz_value &&
1790 !strcasecmp( "t", psz_name ) )
1792 if( ppi_k_durations && ppi_k_run_lengths )
1796 if( *ppi_k_durations )
1798 *ppi_k_durations = (uint32_t *)
1799 realloc( *ppi_k_durations,
1800 *pi_k_runs * sizeof( uint32_t ) );
1802 else if( *pi_k_runs == 1 )
1804 *ppi_k_durations = (uint32_t *)
1805 malloc( *pi_k_runs * sizeof( uint32_t ) );
1808 if( *ppi_k_run_lengths )
1810 *ppi_k_run_lengths = (uint32_t *)
1811 realloc( *ppi_k_run_lengths,
1812 *pi_k_runs * sizeof( uint32_t ) );
1814 else if( *pi_k_runs == 1 )
1816 *ppi_k_run_lengths = (uint32_t *)
1817 malloc( *pi_k_runs * sizeof( uint32_t ) );
1819 if( *ppi_k_durations )
1820 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1822 if( *ppi_k_run_lengths )
1823 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1831 static int ProcessNodes( filter_t *p_filter,
1832 xml_reader_t *p_xml_reader,
1833 text_style_t *p_font_style,
1838 uint32_t **ppi_run_lengths,
1839 ft_style_t ***ppp_styles,
1842 uint32_t *pi_k_runs,
1843 uint32_t **ppi_k_run_lengths,
1844 uint32_t **ppi_k_durations )
1846 int rv = VLC_SUCCESS;
1847 filter_sys_t *p_sys = p_filter->p_sys;
1848 uint32_t *psz_text_orig = psz_text;
1849 font_stack_t *p_fonts = NULL;
1853 char *psz_node = NULL;
1855 bool b_italic = false;
1856 bool b_bold = false;
1857 bool b_uline = false;
1859 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1860 i_scale = val.i_int;
1864 rv = PushFont( &p_fonts,
1865 p_font_style->psz_fontname,
1866 p_font_style->i_font_size * i_scale / 1000,
1867 (p_font_style->i_font_color & 0xffffff) |
1868 ((p_font_style->i_font_alpha & 0xff) << 24),
1869 (p_font_style->i_karaoke_background_color & 0xffffff) |
1870 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1872 if( p_font_style->i_style_flags & STYLE_BOLD )
1874 if( p_font_style->i_style_flags & STYLE_ITALIC )
1876 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1881 rv = PushFont( &p_fonts,
1887 if( rv != VLC_SUCCESS )
1890 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1892 switch ( xml_ReaderNodeType( p_xml_reader ) )
1894 case XML_READER_NONE:
1896 case XML_READER_ENDELEM:
1897 psz_node = xml_ReaderName( p_xml_reader );
1901 if( !strcasecmp( "font", psz_node ) )
1902 PopFont( &p_fonts );
1903 else if( !strcasecmp( "b", psz_node ) )
1905 else if( !strcasecmp( "i", psz_node ) )
1907 else if( !strcasecmp( "u", psz_node ) )
1913 case XML_READER_STARTELEM:
1914 psz_node = xml_ReaderName( p_xml_reader );
1917 if( !strcasecmp( "font", psz_node ) )
1918 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
1919 else if( !strcasecmp( "b", psz_node ) )
1921 else if( !strcasecmp( "i", psz_node ) )
1923 else if( !strcasecmp( "u", psz_node ) )
1925 else if( !strcasecmp( "br", psz_node ) )
1927 SetupLine( p_filter, "\n", &psz_text,
1928 pi_runs, ppi_run_lengths, ppp_styles,
1929 GetStyleFromFontStack( p_sys,
1935 else if( !strcasecmp( "k", psz_node ) )
1937 /* Only valid in karaoke */
1940 if( *pi_k_runs > 0 )
1942 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
1943 *pi_k_runs, *ppi_k_run_lengths );
1945 SetupKaraoke( p_xml_reader, pi_k_runs,
1946 ppi_k_run_lengths, ppi_k_durations );
1953 case XML_READER_TEXT:
1954 psz_node = xml_ReaderValue( p_xml_reader );
1957 /* Turn any multiple-whitespaces into single spaces */
1958 char *s = strpbrk( psz_node, "\t\r\n " );
1961 int i_whitespace = strspn( s, "\t\r\n " );
1963 if( i_whitespace > 1 )
1966 strlen( s ) - i_whitespace + 1 );
1969 s = strpbrk( s, "\t\r\n " );
1971 SetupLine( p_filter, psz_node, &psz_text,
1972 pi_runs, ppi_run_lengths, ppp_styles,
1973 GetStyleFromFontStack( p_sys,
1982 if( rv != VLC_SUCCESS )
1984 psz_text = psz_text_orig;
1990 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
1991 *pi_k_runs, *ppi_k_run_lengths );
1994 *pi_len = psz_text - psz_text_orig;
1996 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2001 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2005 for( k=0; k < p_sys->i_font_attachments; k++ )
2007 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2009 FT_Face p_face = NULL;
2011 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2019 bool match = !strcasecmp( p_face->family_name,
2020 p_style->psz_fontname );
2022 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2023 match = match && p_style->b_bold;
2025 match = match && !p_style->b_bold;
2027 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2028 match = match && p_style->b_italic;
2030 match = match && !p_style->b_italic;
2038 FT_Done_Face( p_face );
2043 return VLC_EGENERIC;
2046 static int ProcessLines( filter_t *p_filter,
2051 uint32_t *pi_run_lengths,
2052 ft_style_t **pp_styles,
2053 line_desc_t **pp_lines,
2055 FT_Vector *p_result,
2059 uint32_t *pi_k_run_lengths,
2060 uint32_t *pi_k_durations )
2062 filter_sys_t *p_sys = p_filter->p_sys;
2063 ft_style_t **pp_char_styles;
2064 int *p_new_positions = NULL;
2065 int8_t *p_levels = NULL;
2066 uint8_t *pi_karaoke_bar = NULL;
2070 /* Assign each character in the text string its style explicitly, so that
2071 * after the characters have been shuffled around by Fribidi, we can re-apply
2072 * the styles, and to simplify the calculation of runs within a line.
2074 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2075 if( !pp_char_styles )
2080 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2081 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2082 * we just won't be able to display the progress bar; at least we'll
2088 for( j = 0; j < i_runs; j++ )
2089 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2090 pp_char_styles[ i++ ] = pp_styles[ j ];
2092 #if defined(HAVE_FRIBIDI)
2094 ft_style_t **pp_char_styles_new;
2095 int *p_old_positions;
2096 uint32_t *p_fribidi_string;
2097 int start_pos, pos = 0;
2099 pp_char_styles_new = (ft_style_t **)
2100 malloc( i_len * sizeof( ft_style_t * ));
2102 p_fribidi_string = (uint32_t *)
2103 malloc( (i_len + 1) * sizeof(uint32_t) );
2104 p_old_positions = (int *)
2105 malloc( (i_len + 1) * sizeof( int ) );
2106 p_new_positions = (int *)
2107 malloc( (i_len + 1) * sizeof( int ) );
2108 p_levels = (int8_t *)
2109 malloc( (i_len + 1) * sizeof( int8_t ) );
2111 if( ! pp_char_styles_new ||
2112 ! p_fribidi_string ||
2113 ! p_old_positions ||
2114 ! p_new_positions ||
2118 free( p_old_positions );
2119 free( p_new_positions );
2120 free( p_fribidi_string );
2121 free( pp_char_styles_new );
2122 free( pi_karaoke_bar );
2124 free( pp_char_styles );
2128 /* Do bidi conversion line-by-line */
2131 while(pos < i_len) {
2132 if (psz_text[pos] != '\n')
2134 p_fribidi_string[pos] = psz_text[pos];
2135 pp_char_styles_new[pos] = pp_char_styles[pos];
2136 p_new_positions[pos] = pos;
2141 while(pos < i_len) {
2142 if (psz_text[pos] == '\n')
2146 if (pos > start_pos)
2148 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2149 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2150 pos - start_pos, &base_dir,
2151 (FriBidiChar*)p_fribidi_string + start_pos,
2152 p_new_positions + start_pos,
2154 p_levels + start_pos );
2155 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2157 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2158 p_old_positions[ j - start_pos ] ];
2159 p_new_positions[ j ] += start_pos;
2163 free( p_old_positions );
2164 free( pp_char_styles );
2165 pp_char_styles = pp_char_styles_new;
2166 psz_text = p_fribidi_string;
2167 p_fribidi_string[ i_len ] = 0;
2170 /* Work out the karaoke */
2171 if( pi_karaoke_bar )
2173 int64_t i_last_duration = 0;
2174 int64_t i_duration = 0;
2175 int64_t i_start_pos = 0;
2176 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2178 for( k = 0; k< i_k_runs; k++ )
2180 double fraction = 0.0;
2182 i_duration += pi_k_durations[ k ];
2184 if( i_duration < i_elapsed )
2186 /* Completely finished this run-length -
2187 * let it render normally */
2191 else if( i_elapsed < i_last_duration )
2193 /* Haven't got up to this segment yet -
2194 * render it completely in karaoke BG mode */
2200 /* Partway through this run */
2202 fraction = (double)(i_elapsed - i_last_duration) /
2203 (double)pi_k_durations[ k ];
2205 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2207 double shade = pi_k_run_lengths[ k ] * fraction;
2209 if( p_new_positions )
2210 j = p_new_positions[ i_start_pos + i ];
2212 j = i_start_pos + i;
2214 if( i < (uint32_t)shade )
2215 pi_karaoke_bar[ j ] = 0xff;
2216 else if( (double)i > shade )
2217 pi_karaoke_bar[ j ] = 0x00;
2220 shade -= (int)shade;
2221 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2222 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2226 i_last_duration = i_duration;
2227 i_start_pos += pi_k_run_lengths[ k ];
2231 free( p_new_positions );
2233 FT_Vector tmp_result;
2235 line_desc_t *p_line = NULL;
2236 line_desc_t *p_prev = NULL;
2242 p_result->x = p_result->y = 0;
2243 tmp_result.x = tmp_result.y = 0;
2246 for( k = 0; k <= (uint32_t) i_len; k++ )
2248 if( ( k == (uint32_t) i_len ) ||
2250 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2252 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2254 /* End of the current style run */
2255 FT_Face p_face = NULL;
2258 /* Look for a match amongst our attachments first */
2259 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2263 char *psz_fontfile = NULL;
2265 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2266 if( p_sys->b_fontconfig_ok )
2268 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2269 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2270 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2271 p_style->psz_fontname,
2276 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2278 if( psz_fontfile && ! *psz_fontfile )
2280 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2281 " so using default font", p_style->psz_fontname,
2282 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2283 (p_style->b_bold ? "(Bold)" :
2284 (p_style->b_italic ? "(Italic)" : ""))) );
2285 free( psz_fontfile );
2286 psz_fontfile = NULL;
2291 if( FT_New_Face( p_sys->p_library,
2292 psz_fontfile, i_idx, &p_face ) )
2294 free( psz_fontfile );
2295 free( pp_char_styles );
2296 #if defined(HAVE_FRIBIDI)
2299 free( pi_karaoke_bar );
2300 return VLC_EGENERIC;
2302 free( psz_fontfile );
2306 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2308 /* We've loaded a font face which is unhelpful for actually
2309 * rendering text - fallback to the default one.
2311 FT_Done_Face( p_face );
2315 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2316 ft_encoding_unicode ) ||
2317 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2318 p_style->i_font_size ) )
2320 if( p_face ) FT_Done_Face( p_face );
2321 free( pp_char_styles );
2322 #if defined(HAVE_FRIBIDI)
2325 free( pi_karaoke_bar );
2326 return VLC_EGENERIC;
2328 p_sys->i_use_kerning =
2329 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2332 uint32_t *psz_unicode = (uint32_t *)
2333 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2336 if( p_face ) FT_Done_Face( p_face );
2337 free( pp_char_styles );
2338 free( psz_unicode );
2339 #if defined(HAVE_FRIBIDI)
2342 free( pi_karaoke_bar );
2345 memcpy( psz_unicode, psz_text + i_prev,
2346 sizeof( uint32_t ) * ( k - i_prev ) );
2347 psz_unicode[ k - i_prev ] = 0;
2348 while( *psz_unicode )
2352 if( !(p_line = NewLine( i_len - i_prev)) )
2354 if( p_face ) FT_Done_Face( p_face );
2355 free( pp_char_styles );
2356 free( psz_unicode );
2357 #if defined(HAVE_FRIBIDI)
2360 free( pi_karaoke_bar );
2363 /* New Color mode only works in YUVA rendering mode --
2364 * (RGB mode has palette constraints on it). We therefore
2365 * need to populate the legacy colour fields also.
2367 p_line->b_new_color_mode = true;
2368 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2369 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2370 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2371 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2372 p_line->p_next = NULL;
2374 i_pen_y += tmp_result.y;
2378 if( p_prev ) p_prev->p_next = p_line;
2379 else *pp_lines = p_line;
2382 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2383 p_style->i_font_color, p_style->b_underline,
2384 p_style->i_karaoke_bg_color,
2385 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2386 &tmp_result ) != VLC_SUCCESS )
2388 if( p_face ) FT_Done_Face( p_face );
2389 free( pp_char_styles );
2390 free( psz_unicode );
2391 #if defined(HAVE_FRIBIDI)
2394 free( pi_karaoke_bar );
2395 return VLC_EGENERIC;
2400 p_result->x = __MAX( p_result->x, tmp_result.x );
2401 p_result->y += tmp_result.y;
2406 if( *psz_unicode == '\n')
2410 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2412 *c_ptr = *(c_ptr+1);
2417 free( psz_unicode );
2418 if( p_face ) FT_Done_Face( p_face );
2422 free( pp_char_styles );
2423 #if defined(HAVE_FRIBIDI)
2428 p_result->x = __MAX( p_result->x, tmp_result.x );
2429 p_result->y += tmp_result.y;
2432 if( pi_karaoke_bar )
2435 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2437 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2439 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2443 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2445 /* 100% BG colour will render faster if we
2446 * instead make it 100% FG colour, so leave
2447 * the ratio alone and copy the value across
2449 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2453 if( pi_karaoke_bar[ i ] & 0x80 )
2455 /* Swap Left and Right sides over for Right aligned
2456 * language text (eg. Arabic, Hebrew)
2458 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2460 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2461 p_line->p_bg_rgb[ k ] = i_tmp;
2463 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2466 /* Jump over the '\n' at the line-end */
2469 free( pi_karaoke_bar );
2475 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2476 subpicture_region_t *p_region_in )
2478 int rv = VLC_SUCCESS;
2479 stream_t *p_sub = NULL;
2480 xml_t *p_xml = NULL;
2481 xml_reader_t *p_xml_reader = NULL;
2483 if( !p_region_in || !p_region_in->psz_html )
2484 return VLC_EGENERIC;
2486 /* Reset the default fontsize in case screen metrics have changed */
2487 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2489 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2490 (uint8_t *) p_region_in->psz_html,
2491 strlen( p_region_in->psz_html ),
2495 p_xml = xml_Create( p_filter );
2498 bool b_karaoke = false;
2500 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2503 /* Look for Root Node */
2504 if( xml_ReaderRead( p_xml_reader ) == 1 )
2506 char *psz_node = xml_ReaderName( p_xml_reader );
2508 if( !strcasecmp( "karaoke", psz_node ) )
2510 /* We're going to have to render the text a number
2511 * of times to show the progress marker on the text.
2513 var_SetBool( p_filter, "text-rerender", true );
2516 else if( !strcasecmp( "text", psz_node ) )
2522 /* Only text and karaoke tags are supported */
2523 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2524 xml_ReaderDelete( p_xml, p_xml_reader );
2525 p_xml_reader = NULL;
2537 uint32_t i_runs = 0;
2538 uint32_t i_k_runs = 0;
2539 uint32_t *pi_run_lengths = NULL;
2540 uint32_t *pi_k_run_lengths = NULL;
2541 uint32_t *pi_k_durations = NULL;
2542 ft_style_t **pp_styles = NULL;
2544 line_desc_t *p_lines = NULL;
2546 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2547 sizeof( uint32_t ) );
2552 rv = ProcessNodes( p_filter, p_xml_reader,
2553 p_region_in->p_style, psz_text, &i_len,
2554 &i_runs, &pi_run_lengths, &pp_styles,
2555 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2558 p_region_out->i_x = p_region_in->i_x;
2559 p_region_out->i_y = p_region_in->i_y;
2561 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2563 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2564 pi_run_lengths, pp_styles, &p_lines, &result,
2565 b_karaoke, i_k_runs, pi_k_run_lengths,
2569 for( k=0; k<i_runs; k++)
2570 DeleteStyle( pp_styles[k] );
2572 free( pi_run_lengths );
2575 /* Don't attempt to render text that couldn't be layed out
2578 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2580 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2582 Render( p_filter, p_region_out, p_lines,
2583 result.x, result.y );
2587 RenderYUVA( p_filter, p_region_out, p_lines,
2588 result.x, result.y );
2592 FreeLines( p_lines );
2594 xml_ReaderDelete( p_xml, p_xml_reader );
2596 xml_Delete( p_xml );
2598 stream_Delete( p_sub );
2604 static char* FontConfig_Select( FcConfig* priv, const char* family,
2605 bool b_bold, bool b_italic, int *i_idx )
2608 FcPattern *pat, *p_pat;
2612 pat = FcPatternCreate();
2613 if (!pat) return NULL;
2615 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2616 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2617 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2618 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2620 FcDefaultSubstitute( pat );
2622 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2624 FcPatternDestroy( pat );
2628 p_pat = FcFontMatch( priv, pat, &result );
2629 FcPatternDestroy( pat );
2630 if( !p_pat ) return NULL;
2632 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2633 || ( val_b != FcTrue ) )
2635 FcPatternDestroy( p_pat );
2638 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2643 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2645 FcPatternDestroy( p_pat );
2650 if( strcasecmp((const char*)val_s, family ) != 0 )
2651 msg_Warn( p_filter, "fontconfig: selected font family is not"
2652 "the requested one: '%s' != '%s'\n",
2653 (const char*)val_s, family );
2656 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2658 FcPatternDestroy( p_pat );
2662 FcPatternDestroy( p_pat );
2663 return strdup( (const char*)val_s );
2667 static void FreeLine( line_desc_t *p_line )
2670 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2672 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2674 free( p_line->pp_glyphs );
2675 free( p_line->p_glyph_pos );
2676 free( p_line->p_fg_rgb );
2677 free( p_line->p_bg_rgb );
2678 free( p_line->p_fg_bg_ratio );
2679 free( p_line->pi_underline_offset );
2680 free( p_line->pi_underline_thickness );
2684 static void FreeLines( line_desc_t *p_lines )
2686 line_desc_t *p_line, *p_next;
2688 if( !p_lines ) return;
2690 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2692 p_next = p_line->p_next;
2697 static line_desc_t *NewLine( int i_count )
2699 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2701 if( !p_line ) return NULL;
2702 p_line->i_height = 0;
2703 p_line->i_width = 0;
2704 p_line->p_next = NULL;
2706 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2707 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2708 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2709 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2710 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2711 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2712 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2713 if( ( p_line->pp_glyphs == NULL ) ||
2714 ( p_line->p_glyph_pos == NULL ) ||
2715 ( p_line->p_fg_rgb == NULL ) ||
2716 ( p_line->p_bg_rgb == NULL ) ||
2717 ( p_line->p_fg_bg_ratio == NULL ) ||
2718 ( p_line->pi_underline_offset == NULL ) ||
2719 ( p_line->pi_underline_thickness == NULL ) )
2721 free( p_line->pi_underline_thickness );
2722 free( p_line->pi_underline_offset );
2723 free( p_line->p_fg_rgb );
2724 free( p_line->p_bg_rgb );
2725 free( p_line->p_fg_bg_ratio );
2726 free( p_line->p_glyph_pos );
2727 free( p_line->pp_glyphs );
2731 p_line->pp_glyphs[0] = NULL;
2732 p_line->b_new_color_mode = false;
2737 static int GetFontSize( filter_t *p_filter )
2739 filter_sys_t *p_sys = p_filter->p_sys;
2743 if( p_sys->i_default_font_size )
2745 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2746 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2748 i_size = p_sys->i_default_font_size;
2752 var_Get( p_filter, "freetype-rel-fontsize", &val );
2755 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2756 p_filter->p_sys->i_display_height =
2757 p_filter->fmt_out.video.i_height;
2762 msg_Warn( p_filter, "invalid fontsize, using 12" );
2763 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2764 i_size = 12 * val.i_int / 1000;
2771 static int SetFontSize( filter_t *p_filter, int i_size )
2773 filter_sys_t *p_sys = p_filter->p_sys;
2777 i_size = GetFontSize( p_filter );
2779 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2782 p_sys->i_font_size = i_size;
2784 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2786 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2787 return VLC_EGENERIC;
2793 static void YUVFromRGB( uint32_t i_argb,
2794 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2796 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2797 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2798 int i_blue = ( i_argb & 0x000000ff );
2800 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2801 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2802 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2803 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2804 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2805 -585 * i_blue + 4096 + 1048576) >> 13, 240);