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_DEFAULT_COLOR 0x00ffffff
278 #define TR_FONT_STYLE_PTR ft_style_t *
280 #include "text_renderer.h"
282 /*****************************************************************************
283 * Create: allocates osd-text video thread output method
284 *****************************************************************************
285 * This function allocates and initializes a Clone vout method.
286 *****************************************************************************/
287 static int Create( vlc_object_t *p_this )
289 filter_t *p_filter = (filter_t *)p_this;
291 char *psz_fontfile = NULL;
295 /* Allocate structure */
296 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
300 p_sys->p_library = 0;
301 p_sys->i_font_size = 0;
302 p_sys->i_display_height = 0;
304 var_Create( p_filter, "freetype-font",
305 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
306 var_Create( p_filter, "freetype-fontsize",
307 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
308 var_Create( p_filter, "freetype-rel-fontsize",
309 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
310 var_Create( p_filter, "freetype-opacity",
311 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
312 var_Create( p_filter, "freetype-effect",
313 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
314 var_Get( p_filter, "freetype-opacity", &val );
315 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
316 var_Create( p_filter, "freetype-color",
317 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
318 var_Get( p_filter, "freetype-color", &val );
319 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
320 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
322 /* Look what method was requested */
323 var_Get( p_filter, "freetype-font", &val );
324 psz_fontfile = val.psz_string;
325 if( !psz_fontfile || !*psz_fontfile )
327 free( psz_fontfile );
328 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
332 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
333 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
334 #elif defined(__APPLE__)
335 strcpy( psz_fontfile, DEFAULT_FONT );
337 msg_Err( p_filter, "user didn't specify a font" );
342 i_error = FT_Init_FreeType( &p_sys->p_library );
345 msg_Err( p_filter, "couldn't initialize freetype" );
348 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
350 if( i_error == FT_Err_Unknown_File_Format )
352 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
357 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
361 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
364 msg_Err( p_filter, "font has no unicode translation table" );
368 #ifdef HAVE_FONTCONFIG
369 p_sys->b_fontconfig_ok = false;
370 p_sys->p_fontconfig = NULL;
371 p_sys->p_fontbuilder = FontBuilderAttach( p_filter, &p_sys->p_fontconfig_lock );
374 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
376 var_Get( p_filter, "freetype-fontsize", &val );
377 p_sys->i_default_font_size = val.i_int;
378 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
380 free( psz_fontfile );
382 p_sys->pp_font_attachments = NULL;
383 p_sys->i_font_attachments = 0;
385 p_filter->pf_render_text = RenderText;
386 #ifdef HAVE_FONTCONFIG
387 p_filter->pf_render_html = RenderHtml;
389 p_filter->pf_render_html = NULL;
392 LoadFontsFromAttachments( p_filter );
397 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
398 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
399 free( psz_fontfile );
404 /*****************************************************************************
405 * Destroy: destroy Clone video thread output method
406 *****************************************************************************
407 * Clean up all data and library connections
408 *****************************************************************************/
409 static void Destroy( vlc_object_t *p_this )
411 filter_t *p_filter = (filter_t *)p_this;
412 filter_sys_t *p_sys = p_filter->p_sys;
414 if( p_sys->pp_font_attachments )
418 for( k = 0; k < p_sys->i_font_attachments; k++ )
419 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
421 free( p_sys->pp_font_attachments );
424 #ifdef HAVE_FONTCONFIG
425 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
428 /* FcFini asserts calling the subfunction FcCacheFini()
429 * even if no other library functions have been made since FcInit(),
430 * so don't call it. */
432 FT_Done_Face( p_sys->p_face );
433 FT_Done_FreeType( p_sys->p_library );
437 #ifdef HAVE_FONTCONFIG
438 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock )
440 /* Check for an existing Fontbuilder thread */
441 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
442 vlc_object_t *p_fontbuilder =
443 vlc_object_find_name( p_filter->p_libvlc,
444 "fontlist builder", FIND_CHILD );
448 /* Create the FontBuilderThread thread as a child of a top-level
449 * object, so that it can survive the destruction of the
450 * freetype object - the fontlist only needs to be built once,
451 * and calling the fontbuild a second time while the first is
452 * still in progress can cause thread instabilities.
454 * XXX The fontbuilder will be destroy as soon as it is unused.
457 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
458 sizeof(vlc_object_t) );
461 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
462 p_fontbuilder->p_private = NULL;
463 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
465 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
467 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
468 var_SetBool( p_fontbuilder, "build-done", false );
470 if( vlc_thread_create( p_fontbuilder,
473 VLC_THREAD_PRIORITY_LOW,
476 msg_Warn( p_filter, "fontconfig database builder thread can't "
477 "be launched. Font styling support will be limited." );
483 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
484 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
486 vlc_mutex_unlock( p_lock );
488 return p_fontbuilder;
490 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
492 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
495 const bool b_alive = vlc_object_alive( p_fontbuilder );
497 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
499 /* We wait for the thread on the first FontBuilderDetach */
502 vlc_object_kill( p_fontbuilder );
503 vlc_mutex_unlock( lock );
505 /* We need to unlock otherwise we may not join (the thread waiting
506 * for the lock). It is safe to unlock as no one else will try a
507 * join and we have a reference on the object) */
508 vlc_thread_join( p_fontbuilder );
510 vlc_mutex_lock( lock );
512 vlc_object_release( p_fontbuilder );
514 vlc_mutex_unlock( lock );
516 static void* FontBuilderThread( vlc_object_t *p_this )
518 FcConfig *p_fontconfig = FcInitLoadConfig();
520 vlc_thread_ready( p_this );
525 int canc = vlc_savecancel ();
527 //msg_Dbg( p_this, "Building font database..." );
528 msg_Dbg( p_this, "Building font database..." );
530 if(! FcConfigBuildFonts( p_fontconfig ))
532 /* Don't destroy the fontconfig object - we won't be able to do
533 * italics or bold or change the font face, but we will still
534 * be able to do underline and change the font size.
536 msg_Err( p_this, "fontconfig database can't be built. "
537 "Font styling won't be available" );
541 msg_Dbg( p_this, "Finished building font database." );
542 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
544 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
545 p_this->p_private = p_fontconfig;
546 vlc_mutex_unlock( p_lock );
548 var_SetBool( p_this, "build-done", true );
549 vlc_restorecancel (canc);
553 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
555 filter_sys_t *p_sys = p_filter->p_sys;
557 p_sys->p_fontconfig = p_fontbuilder->p_private;
558 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
560 static void FontBuilderDestructor( vlc_object_t *p_this )
562 FcConfig *p_fontconfig = p_this->p_private;
565 FcConfigDestroy( p_fontconfig );
567 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
568 vlc_value_t oldval, vlc_value_t newval, void *param )
570 filter_t *p_filter = param;
574 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
576 FontBuilderGetFcConfig( p_filter, p_this );
578 vlc_mutex_unlock( p_lock );
587 /*****************************************************************************
588 * Make any TTF/OTF fonts present in the attachments of the media file
589 * and store them for later use by the FreeType Engine
590 *****************************************************************************/
591 static int LoadFontsFromAttachments( filter_t *p_filter )
593 filter_sys_t *p_sys = p_filter->p_sys;
594 input_thread_t *p_input;
595 input_attachment_t **pp_attachments;
596 int i_attachments_cnt;
598 int rv = VLC_SUCCESS;
600 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
604 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
606 vlc_object_release(p_input);
610 p_sys->i_font_attachments = 0;
611 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
612 if(! p_sys->pp_font_attachments )
615 for( k = 0; k < i_attachments_cnt; k++ )
617 input_attachment_t *p_attach = pp_attachments[k];
619 if( p_sys->pp_font_attachments )
621 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
622 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
623 ( p_attach->i_data > 0 ) &&
624 ( p_attach->p_data != NULL ) )
626 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
630 vlc_input_attachment_Delete( p_attach );
635 vlc_input_attachment_Delete( p_attach );
638 free( pp_attachments );
640 vlc_object_release(p_input);
645 /*****************************************************************************
646 * Render: place string in picture
647 *****************************************************************************
648 * This function merges the previously rendered freetype glyphs into a picture
649 *****************************************************************************/
650 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
651 line_desc_t *p_line, int i_width, int i_height )
653 static const uint8_t pi_gamma[16] =
654 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
655 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
659 int i, x, y, i_pitch;
660 uint8_t i_y; /* YUV values, derived from incoming RGB */
663 /* Create a new subpicture region */
664 memset( &fmt, 0, sizeof(video_format_t) );
665 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
667 fmt.i_width = fmt.i_visible_width = i_width + 4;
668 fmt.i_height = fmt.i_visible_height = i_height + 4;
669 if( p_region->fmt.i_visible_width > 0 )
670 fmt.i_visible_width = p_region->fmt.i_visible_width;
671 if( p_region->fmt.i_visible_height > 0 )
672 fmt.i_visible_height = p_region->fmt.i_visible_height;
673 fmt.i_x_offset = fmt.i_y_offset = 0;
675 assert( !p_region->p_picture );
676 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
677 if( !p_region->p_picture )
681 /* Calculate text color components */
682 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
683 25 * p_line->i_blue + 128) >> 8) + 16;
684 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
685 112 * p_line->i_blue + 128) >> 8) + 128;
686 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
687 18 * p_line->i_blue + 128) >> 8) + 128;
690 fmt.p_palette->i_entries = 16;
691 for( i = 0; i < 8; i++ )
693 fmt.p_palette->palette[i][0] = 0;
694 fmt.p_palette->palette[i][1] = 0x80;
695 fmt.p_palette->palette[i][2] = 0x80;
696 fmt.p_palette->palette[i][3] = pi_gamma[i];
697 fmt.p_palette->palette[i][3] =
698 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
700 for( i = 8; i < fmt.p_palette->i_entries; i++ )
702 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
703 fmt.p_palette->palette[i][1] = i_u;
704 fmt.p_palette->palette[i][2] = i_v;
705 fmt.p_palette->palette[i][3] = pi_gamma[i];
706 fmt.p_palette->palette[i][3] =
707 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
710 p_dst = p_region->p_picture->Y_PIXELS;
711 i_pitch = p_region->p_picture->Y_PITCH;
713 /* Initialize the region pixels */
714 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
716 for( ; p_line != NULL; p_line = p_line->p_next )
718 int i_glyph_tmax = 0;
719 int i_bitmap_offset, i_offset, i_align_offset = 0;
720 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
722 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
723 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
726 if( p_line->i_width < i_width )
728 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
730 i_align_offset = i_width - p_line->i_width;
732 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
734 i_align_offset = ( i_width - p_line->i_width ) / 2;
738 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
740 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
742 i_offset = ( p_line->p_glyph_pos[ i ].y +
743 i_glyph_tmax - p_glyph->top + 2 ) *
744 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
747 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
749 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
751 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
753 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
760 /* Outlining (find something better than nearest neighbour filtering ?) */
763 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
764 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
765 uint8_t left, current;
767 for( y = 1; y < (int)fmt.i_height - 1; y++ )
769 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
770 p_dst += p_region->p_picture->Y_PITCH;
773 for( x = 1; x < (int)fmt.i_width - 1; x++ )
776 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
777 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;
781 memset( p_top, 0, fmt.i_width );
787 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
788 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
789 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
790 int i_glyph_tmax, int i_align_offset,
791 uint8_t i_y, uint8_t i_u, uint8_t i_v,
792 subpicture_region_t *p_region)
796 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
798 p_dst_y = p_region->p_picture->Y_PIXELS;
799 p_dst_u = p_region->p_picture->U_PIXELS;
800 p_dst_v = p_region->p_picture->V_PIXELS;
801 p_dst_a = p_region->p_picture->A_PIXELS;
802 i_pitch = p_region->p_picture->A_PITCH;
804 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
805 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
807 for( y = 0; y < i_line_thickness; y++ )
809 int i_extra = p_this_glyph->bitmap.width;
813 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
814 (p_this_glyph_pos->x + p_this_glyph->left);
816 for( x = 0; x < i_extra; x++ )
820 /* break the underline around the tails of any glyphs which cross it */
821 for( z = x - i_line_thickness;
822 z < x + i_line_thickness && b_ok;
825 if( p_next_glyph && ( z >= i_extra ) )
827 int i_row = i_line_offset + p_next_glyph->top + y;
829 if( ( p_next_glyph->bitmap.rows > i_row ) &&
830 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
835 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
837 int i_row = i_line_offset + p_this_glyph->top + y;
839 if( ( p_this_glyph->bitmap.rows > i_row ) &&
840 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
849 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
850 p_dst_u[i_offset+x] = i_u;
851 p_dst_v[i_offset+x] = i_v;
852 p_dst_a[i_offset+x] = 255;
859 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
861 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
862 int i_pitch = p_region->p_picture->A_PITCH;
865 for( ; p_line != NULL; p_line = p_line->p_next )
867 int i_glyph_tmax=0, i = 0;
868 int i_bitmap_offset, i_offset, i_align_offset = 0;
869 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
871 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
872 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
875 if( p_line->i_width < i_width )
877 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
879 i_align_offset = i_width - p_line->i_width;
881 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
883 i_align_offset = ( i_width - p_line->i_width ) / 2;
887 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
889 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
891 i_offset = ( p_line->p_glyph_pos[ i ].y +
892 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
893 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
894 i_align_offset +xoffset;
896 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
898 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
900 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
901 if( p_dst[i_offset+x] <
902 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
904 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
913 /*****************************************************************************
914 * Render: place string in picture
915 *****************************************************************************
916 * This function merges the previously rendered freetype glyphs into a picture
917 *****************************************************************************/
918 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
919 line_desc_t *p_line, int i_width, int i_height )
921 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
923 int i, x, y, i_pitch, i_alpha;
924 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
926 if( i_width == 0 || i_height == 0 )
929 /* Create a new subpicture region */
930 memset( &fmt, 0, sizeof(video_format_t) );
931 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
933 fmt.i_width = fmt.i_visible_width = i_width + 6;
934 fmt.i_height = fmt.i_visible_height = i_height + 6;
935 if( p_region->fmt.i_visible_width > 0 )
936 fmt.i_visible_width = p_region->fmt.i_visible_width;
937 if( p_region->fmt.i_visible_height > 0 )
938 fmt.i_visible_height = p_region->fmt.i_visible_height;
939 fmt.i_x_offset = fmt.i_y_offset = 0;
941 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
942 if( !p_region->p_picture )
946 /* Calculate text color components */
947 YUVFromRGB( (p_line->i_red << 16) |
948 (p_line->i_green << 8) |
951 i_alpha = p_line->i_alpha;
953 p_dst_y = p_region->p_picture->Y_PIXELS;
954 p_dst_u = p_region->p_picture->U_PIXELS;
955 p_dst_v = p_region->p_picture->V_PIXELS;
956 p_dst_a = p_region->p_picture->A_PIXELS;
957 i_pitch = p_region->p_picture->A_PITCH;
959 /* Initialize the region pixels */
960 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
962 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
963 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
964 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
965 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
969 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
970 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
971 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
972 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
974 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
975 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
977 DrawBlack( p_line, i_width, p_region, 0, 0);
978 DrawBlack( p_line, i_width, p_region, -1, 0);
979 DrawBlack( p_line, i_width, p_region, 0, -1);
980 DrawBlack( p_line, i_width, p_region, 1, 0);
981 DrawBlack( p_line, i_width, p_region, 0, 1);
984 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
989 DrawBlack( p_line, i_width, p_region, 1, 1);
991 DrawBlack( p_line, i_width, p_region, -2, 0);
992 DrawBlack( p_line, i_width, p_region, 0, -2);
993 DrawBlack( p_line, i_width, p_region, 2, 0);
994 DrawBlack( p_line, i_width, p_region, 0, 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);
999 DrawBlack( p_line, i_width, p_region, 2, 2);
1001 DrawBlack( p_line, i_width, p_region, -3, 0);
1002 DrawBlack( p_line, i_width, p_region, 0, -3);
1003 DrawBlack( p_line, i_width, p_region, 3, 0);
1004 DrawBlack( p_line, i_width, p_region, 0, 3);
1007 for( ; p_line != NULL; p_line = p_line->p_next )
1009 int i_glyph_tmax = 0;
1010 int i_bitmap_offset, i_offset, i_align_offset = 0;
1011 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1013 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1014 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1017 if( p_line->i_width < i_width )
1019 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1021 i_align_offset = i_width - p_line->i_width;
1023 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1025 i_align_offset = ( i_width - p_line->i_width ) / 2;
1029 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1031 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1033 i_offset = ( p_line->p_glyph_pos[ i ].y +
1034 i_glyph_tmax - p_glyph->top + 3 ) *
1035 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1038 if( p_line->b_new_color_mode )
1040 /* Every glyph can (and in fact must) have its own color */
1041 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1044 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1046 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1048 uint8_t i_y_local = i_y;
1049 uint8_t i_u_local = i_u;
1050 uint8_t i_v_local = i_v;
1052 if( p_line->p_fg_bg_ratio != 0x00 )
1054 int i_split = p_glyph->bitmap.width *
1055 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1059 YUVFromRGB( p_line->p_bg_rgb[ i ],
1060 &i_y_local, &i_u_local, &i_v_local );
1064 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1066 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1067 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1069 p_dst_u[i_offset+x] = i_u;
1070 p_dst_v[i_offset+x] = i_v;
1072 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1073 p_dst_a[i_offset+x] = 0xff;
1076 i_offset += i_pitch;
1079 if( p_line->pi_underline_thickness[ i ] )
1081 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1082 p_line->pi_underline_offset[ i ],
1083 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1084 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1085 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1086 i_glyph_tmax, i_align_offset,
1093 /* Apply the alpha setting */
1094 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1095 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1101 * This function renders a text subpicture region into another one.
1102 * It also calculates the size needed for this string, and renders the
1103 * needed glyphs into memory. It is used as pf_add_string callback in
1104 * the vout method by this module
1106 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1107 subpicture_region_t *p_region_in )
1109 filter_sys_t *p_sys = p_filter->p_sys;
1110 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1111 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1112 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1113 int i_string_length;
1115 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1116 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1126 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1127 psz_string = p_region_in->psz_text;
1128 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1130 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1131 i_scale = val.i_int;
1133 if( p_region_in->p_style )
1135 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1136 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1137 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1141 i_font_color = p_sys->i_font_color;
1142 i_font_alpha = 255 - p_sys->i_font_opacity;
1143 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1146 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1147 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1148 SetFontSize( p_filter, i_font_size );
1150 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1151 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1152 i_blue = i_font_color & 0x000000FF;
1154 result.x = result.y = 0;
1155 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1157 psz_unicode = psz_unicode_orig =
1158 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1159 if( psz_unicode == NULL )
1161 #if defined(WORDS_BIGENDIAN)
1162 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1164 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1166 if( iconv_handle == (vlc_iconv_t)-1 )
1168 msg_Warn( p_filter, "unable to do conversion" );
1174 const char *p_in_buffer = psz_string;
1175 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1176 i_in_bytes = strlen( psz_string );
1177 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1178 i_out_bytes_left = i_out_bytes;
1179 p_out_buffer = (char *)psz_unicode;
1180 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1182 &p_out_buffer, &i_out_bytes_left );
1184 vlc_iconv_close( iconv_handle );
1188 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1189 "bytes left %u", (unsigned)i_in_bytes );
1192 *(uint32_t*)p_out_buffer = 0;
1193 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1196 #if defined(HAVE_FRIBIDI)
1198 uint32_t *p_fribidi_string;
1199 int32_t start_pos, pos = 0;
1201 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1202 if( !p_fribidi_string )
1205 /* Do bidi conversion line-by-line */
1206 while( pos < i_string_length )
1208 while( pos < i_string_length )
1210 i_char = psz_unicode[pos];
1211 if (i_char != '\r' && i_char != '\n')
1213 p_fribidi_string[pos] = i_char;
1217 while( pos < i_string_length )
1219 i_char = psz_unicode[pos];
1220 if (i_char == '\r' || i_char == '\n')
1224 if (pos > start_pos)
1226 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1227 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1230 (FriBidiChar*)p_fribidi_string + start_pos,
1235 free( psz_unicode_orig );
1236 psz_unicode = psz_unicode_orig = p_fribidi_string;
1237 p_fribidi_string[ i_string_length ] = 0;
1241 /* Calculate relative glyph positions and a bounding box for the
1243 if( !(p_line = NewLine( strlen( psz_string ))) )
1246 i_pen_x = i_pen_y = 0;
1248 psz_line_start = psz_unicode;
1250 #define face p_sys->p_face
1251 #define glyph face->glyph
1253 while( *psz_unicode )
1255 i_char = *psz_unicode++;
1256 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1261 if( i_char == '\n' )
1263 psz_line_start = psz_unicode;
1264 if( !(p_next = NewLine( strlen( psz_string ))) )
1266 p_line->p_next = p_next;
1267 p_line->i_width = line.xMax;
1268 p_line->i_height = face->size->metrics.height >> 6;
1269 p_line->pp_glyphs[ i ] = NULL;
1270 p_line->i_alpha = i_font_alpha;
1271 p_line->i_red = i_red;
1272 p_line->i_green = i_green;
1273 p_line->i_blue = i_blue;
1276 result.x = __MAX( result.x, line.xMax );
1277 result.y += face->size->metrics.height >> 6;
1280 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1281 i_pen_y += face->size->metrics.height >> 6;
1283 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1288 i_glyph_index = FT_Get_Char_Index( face, i_char );
1289 if( p_sys->i_use_kerning && i_glyph_index
1293 FT_Get_Kerning( face, i_previous, i_glyph_index,
1294 ft_kerning_default, &delta );
1295 i_pen_x += delta.x >> 6;
1298 p_line->p_glyph_pos[ i ].x = i_pen_x;
1299 p_line->p_glyph_pos[ i ].y = i_pen_y;
1300 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1303 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1307 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1310 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1314 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1315 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1318 FT_Done_Glyph( tmp_glyph );
1321 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1324 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1325 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1326 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1328 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1329 p_line->pp_glyphs[ i ] = NULL;
1331 p_line = NewLine( strlen( psz_string ));
1332 if( p_prev ) p_prev->p_next = p_line;
1333 else p_lines = p_line;
1335 uint32_t *psz_unicode_saved = psz_unicode;
1336 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1340 if( psz_unicode == psz_line_start )
1341 { /* try harder to break that line */
1342 psz_unicode = psz_unicode_saved;
1343 while( psz_unicode > psz_line_start &&
1344 *psz_unicode != '_' && *psz_unicode != '/' &&
1345 *psz_unicode != '\\' && *psz_unicode != '.' )
1350 if( psz_unicode == psz_line_start )
1352 msg_Warn( p_filter, "unbreakable string" );
1357 *psz_unicode = '\n';
1359 psz_unicode = psz_line_start;
1362 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1365 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1366 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1368 i_previous = i_glyph_index;
1369 i_pen_x += glyph->advance.x >> 6;
1373 p_line->i_width = line.xMax;
1374 p_line->i_height = face->size->metrics.height >> 6;
1375 p_line->pp_glyphs[ i ] = NULL;
1376 p_line->i_alpha = i_font_alpha;
1377 p_line->i_red = i_red;
1378 p_line->i_green = i_green;
1379 p_line->i_blue = i_blue;
1380 result.x = __MAX( result.x, line.xMax );
1381 result.y += line.yMax - line.yMin;
1386 p_region_out->i_x = p_region_in->i_x;
1387 p_region_out->i_y = p_region_in->i_y;
1389 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1390 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1392 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1394 free( psz_unicode_orig );
1395 FreeLines( p_lines );
1399 free( psz_unicode_orig );
1400 FreeLines( p_lines );
1401 return VLC_EGENERIC;
1404 #ifdef HAVE_FONTCONFIG
1405 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1406 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1407 bool b_italic, bool b_uline )
1409 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1413 p_style->i_font_size = i_font_size;
1414 p_style->i_font_color = i_font_color;
1415 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1416 p_style->b_italic = b_italic;
1417 p_style->b_bold = b_bold;
1418 p_style->b_underline = b_uline;
1420 p_style->psz_fontname = strdup( psz_fontname );
1425 static void DeleteStyle( ft_style_t *p_style )
1429 free( p_style->psz_fontname );
1434 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1441 if(( s1->i_font_size == s2->i_font_size ) &&
1442 ( s1->i_font_color == s2->i_font_color ) &&
1443 ( s1->b_italic == s2->b_italic ) &&
1444 ( s1->b_bold == s2->b_bold ) &&
1445 ( s1->b_underline == s2->b_underline ) &&
1446 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1453 static void IconvText( filter_t *p_filter, const char *psz_string,
1454 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1456 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1458 /* If memory hasn't been allocated for our output string, allocate it here
1459 * - the calling function must now be responsible for freeing it.
1461 if( !*ppsz_unicode )
1462 *ppsz_unicode = (uint32_t *)
1463 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1465 /* We don't need to handle a NULL pointer in *ppsz_unicode
1466 * if we are instead testing for a non NULL value like we are here */
1470 #if defined(WORDS_BIGENDIAN)
1471 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1473 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1475 if( iconv_handle != (vlc_iconv_t)-1 )
1477 char *p_in_buffer, *p_out_buffer;
1478 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1479 i_in_bytes = strlen( psz_string );
1480 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1481 i_out_bytes_left = i_out_bytes;
1482 p_in_buffer = (char *) psz_string;
1483 p_out_buffer = (char *) *ppsz_unicode;
1484 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1485 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1487 vlc_iconv_close( iconv_handle );
1491 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1492 "bytes left %u", (unsigned)i_in_bytes );
1496 *(uint32_t*)p_out_buffer = 0;
1498 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1503 msg_Warn( p_filter, "unable to do conversion" );
1508 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1509 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1512 ft_style_t *p_style = NULL;
1514 char *psz_fontname = NULL;
1515 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1516 uint32_t i_karaoke_bg_color = i_font_color;
1517 int i_font_size = p_sys->i_font_size;
1519 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1520 &i_font_color, &i_karaoke_bg_color ))
1522 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1523 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1528 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1529 bool b_uline, int i_karaoke_bgcolor,
1530 line_desc_t *p_line, uint32_t *psz_unicode,
1531 int *pi_pen_x, int i_pen_y, int *pi_start,
1532 FT_Vector *p_result )
1537 bool b_first_on_line = true;
1540 int i_pen_x_start = *pi_pen_x;
1542 uint32_t *psz_unicode_start = psz_unicode;
1544 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1546 /* Account for part of line already in position */
1547 for( i=0; i<*pi_start; i++ )
1551 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1552 ft_glyph_bbox_pixels, &glyph_size );
1554 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1555 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1556 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1557 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1563 b_first_on_line = false;
1565 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1571 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1572 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1576 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1577 ft_kerning_default, &delta );
1578 *pi_pen_x += delta.x >> 6;
1580 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1581 p_line->p_glyph_pos[ i ].y = i_pen_y;
1583 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1587 "unable to render text FT_Load_Glyph returned %d", i_error );
1588 p_line->pp_glyphs[ i ] = NULL;
1589 return VLC_EGENERIC;
1591 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1595 "unable to render text FT_Get_Glyph returned %d", i_error );
1596 p_line->pp_glyphs[ i ] = NULL;
1597 return VLC_EGENERIC;
1599 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1600 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1603 FT_Done_Glyph( tmp_glyph );
1608 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1609 p_face->size->metrics.y_scale));
1610 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1611 p_face->size->metrics.y_scale));
1613 p_line->pi_underline_offset[ i ] =
1614 ( aOffset < 0 ) ? -aOffset : aOffset;
1615 p_line->pi_underline_thickness[ i ] =
1616 ( aSize < 0 ) ? -aSize : aSize;
1618 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1619 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1620 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1621 p_line->p_fg_bg_ratio[ i ] = 0x00;
1623 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1624 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1625 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1627 for( ; i >= *pi_start; i-- )
1628 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1631 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1635 if( psz_unicode == psz_unicode_start )
1637 if( b_first_on_line )
1639 msg_Warn( p_filter, "unbreakable string" );
1640 p_line->pp_glyphs[ i ] = NULL;
1641 return VLC_EGENERIC;
1643 *pi_pen_x = i_pen_x_start;
1645 p_line->i_width = line.xMax;
1646 p_line->i_height = __MAX( p_line->i_height,
1647 p_face->size->metrics.height >> 6 );
1648 p_line->pp_glyphs[ i ] = NULL;
1650 p_result->x = __MAX( p_result->x, line.xMax );
1651 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1652 i_yMax - i_yMin ) );
1657 *psz_unicode = '\n';
1659 psz_unicode = psz_unicode_start;
1660 *pi_pen_x = i_pen_x_start;
1668 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1669 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1671 i_previous = i_glyph_index;
1672 *pi_pen_x += p_face->glyph->advance.x >> 6;
1675 p_line->i_width = line.xMax;
1676 p_line->i_height = __MAX( p_line->i_height,
1677 p_face->size->metrics.height >> 6 );
1678 p_line->pp_glyphs[ i ] = NULL;
1680 p_result->x = __MAX( p_result->x, line.xMax );
1681 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1682 line.yMax - line.yMin ) );
1686 /* Get rid of any text processed - if necessary repositioning
1687 * at the start of a new line of text
1691 *psz_unicode_start = '\0';
1693 else if( psz_unicode > psz_unicode_start )
1695 for( i=0; psz_unicode[ i ]; i++ )
1696 psz_unicode_start[ i ] = psz_unicode[ i ];
1697 psz_unicode_start[ i ] = '\0';
1703 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1704 uint32_t **psz_text_out, uint32_t *pi_runs,
1705 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1706 ft_style_t *p_style )
1708 uint32_t i_string_length = 0;
1710 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1711 *psz_text_out += i_string_length;
1713 if( ppp_styles && ppi_run_lengths )
1719 *ppp_styles = (ft_style_t **)
1720 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1722 else if( *pi_runs == 1 )
1724 *ppp_styles = (ft_style_t **)
1725 malloc( *pi_runs * sizeof( ft_style_t * ) );
1728 /* We have just malloc'ed this memory successfully -
1729 * *pi_runs HAS to be within the memory area of *ppp_styles */
1732 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1736 if( *ppi_run_lengths )
1738 *ppi_run_lengths = (uint32_t *)
1739 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1741 else if( *pi_runs == 1 )
1743 *ppi_run_lengths = (uint32_t *)
1744 malloc( *pi_runs * sizeof( uint32_t ) );
1747 /* same remarks here */
1748 if( *ppi_run_lengths )
1750 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1753 /* If we couldn't use the p_style argument due to memory allocation
1754 * problems above, release it here.
1756 if( p_style ) DeleteStyle( p_style );
1759 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1763 for( k=0; k < p_sys->i_font_attachments; k++ )
1765 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1767 FT_Face p_face = NULL;
1769 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1777 bool match = !strcasecmp( p_face->family_name,
1778 p_style->psz_fontname );
1780 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1781 match = match && p_style->b_bold;
1783 match = match && !p_style->b_bold;
1785 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1786 match = match && p_style->b_italic;
1788 match = match && !p_style->b_italic;
1796 FT_Done_Face( p_face );
1801 return VLC_EGENERIC;
1804 static int ProcessLines( filter_t *p_filter,
1809 uint32_t *pi_run_lengths,
1810 ft_style_t **pp_styles,
1811 line_desc_t **pp_lines,
1813 FT_Vector *p_result,
1817 uint32_t *pi_k_run_lengths,
1818 uint32_t *pi_k_durations )
1820 filter_sys_t *p_sys = p_filter->p_sys;
1821 ft_style_t **pp_char_styles;
1822 int *p_new_positions = NULL;
1823 int8_t *p_levels = NULL;
1824 uint8_t *pi_karaoke_bar = NULL;
1828 /* Assign each character in the text string its style explicitly, so that
1829 * after the characters have been shuffled around by Fribidi, we can re-apply
1830 * the styles, and to simplify the calculation of runs within a line.
1832 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1833 if( !pp_char_styles )
1838 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1839 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1840 * we just won't be able to display the progress bar; at least we'll
1846 for( j = 0; j < i_runs; j++ )
1847 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1848 pp_char_styles[ i++ ] = pp_styles[ j ];
1850 #if defined(HAVE_FRIBIDI)
1852 ft_style_t **pp_char_styles_new;
1853 int *p_old_positions;
1854 uint32_t *p_fribidi_string;
1855 int start_pos, pos = 0;
1857 pp_char_styles_new = (ft_style_t **)
1858 malloc( i_len * sizeof( ft_style_t * ));
1860 p_fribidi_string = (uint32_t *)
1861 malloc( (i_len + 1) * sizeof(uint32_t) );
1862 p_old_positions = (int *)
1863 malloc( (i_len + 1) * sizeof( int ) );
1864 p_new_positions = (int *)
1865 malloc( (i_len + 1) * sizeof( int ) );
1866 p_levels = (int8_t *)
1867 malloc( (i_len + 1) * sizeof( int8_t ) );
1869 if( ! pp_char_styles_new ||
1870 ! p_fribidi_string ||
1871 ! p_old_positions ||
1872 ! p_new_positions ||
1876 free( p_old_positions );
1877 free( p_new_positions );
1878 free( p_fribidi_string );
1879 free( pp_char_styles_new );
1880 free( pi_karaoke_bar );
1882 free( pp_char_styles );
1886 /* Do bidi conversion line-by-line */
1889 while(pos < i_len) {
1890 if (psz_text[pos] != '\n')
1892 p_fribidi_string[pos] = psz_text[pos];
1893 pp_char_styles_new[pos] = pp_char_styles[pos];
1894 p_new_positions[pos] = pos;
1899 while(pos < i_len) {
1900 if (psz_text[pos] == '\n')
1904 if (pos > start_pos)
1906 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1907 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1908 pos - start_pos, &base_dir,
1909 (FriBidiChar*)p_fribidi_string + start_pos,
1910 p_new_positions + start_pos,
1912 p_levels + start_pos );
1913 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1915 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1916 p_old_positions[ j - start_pos ] ];
1917 p_new_positions[ j ] += start_pos;
1921 free( p_old_positions );
1922 free( pp_char_styles );
1923 pp_char_styles = pp_char_styles_new;
1924 psz_text = p_fribidi_string;
1925 p_fribidi_string[ i_len ] = 0;
1928 /* Work out the karaoke */
1929 if( pi_karaoke_bar )
1931 int64_t i_last_duration = 0;
1932 int64_t i_duration = 0;
1933 int64_t i_start_pos = 0;
1934 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1936 for( k = 0; k< i_k_runs; k++ )
1938 double fraction = 0.0;
1940 i_duration += pi_k_durations[ k ];
1942 if( i_duration < i_elapsed )
1944 /* Completely finished this run-length -
1945 * let it render normally */
1949 else if( i_elapsed < i_last_duration )
1951 /* Haven't got up to this segment yet -
1952 * render it completely in karaoke BG mode */
1958 /* Partway through this run */
1960 fraction = (double)(i_elapsed - i_last_duration) /
1961 (double)pi_k_durations[ k ];
1963 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1965 double shade = pi_k_run_lengths[ k ] * fraction;
1967 if( p_new_positions )
1968 j = p_new_positions[ i_start_pos + i ];
1970 j = i_start_pos + i;
1972 if( i < (uint32_t)shade )
1973 pi_karaoke_bar[ j ] = 0xff;
1974 else if( (double)i > shade )
1975 pi_karaoke_bar[ j ] = 0x00;
1978 shade -= (int)shade;
1979 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1980 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1984 i_last_duration = i_duration;
1985 i_start_pos += pi_k_run_lengths[ k ];
1989 free( p_new_positions );
1991 FT_Vector tmp_result;
1993 line_desc_t *p_line = NULL;
1994 line_desc_t *p_prev = NULL;
2000 p_result->x = p_result->y = 0;
2001 tmp_result.x = tmp_result.y = 0;
2004 for( k = 0; k <= (uint32_t) i_len; k++ )
2006 if( ( k == (uint32_t) i_len ) ||
2008 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2010 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2012 /* End of the current style run */
2013 FT_Face p_face = NULL;
2016 /* Look for a match amongst our attachments first */
2017 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2021 char *psz_fontfile = NULL;
2023 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2024 if( p_sys->b_fontconfig_ok )
2026 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2027 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2028 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2029 p_style->psz_fontname,
2034 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2036 if( psz_fontfile && ! *psz_fontfile )
2038 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2039 " so using default font", p_style->psz_fontname,
2040 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2041 (p_style->b_bold ? "(Bold)" :
2042 (p_style->b_italic ? "(Italic)" : ""))) );
2043 free( psz_fontfile );
2044 psz_fontfile = NULL;
2049 if( FT_New_Face( p_sys->p_library,
2050 psz_fontfile, i_idx, &p_face ) )
2052 free( psz_fontfile );
2053 free( pp_char_styles );
2054 #if defined(HAVE_FRIBIDI)
2057 free( pi_karaoke_bar );
2058 return VLC_EGENERIC;
2060 free( psz_fontfile );
2064 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2066 /* We've loaded a font face which is unhelpful for actually
2067 * rendering text - fallback to the default one.
2069 FT_Done_Face( p_face );
2073 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2074 ft_encoding_unicode ) ||
2075 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2076 p_style->i_font_size ) )
2078 if( p_face ) FT_Done_Face( p_face );
2079 free( pp_char_styles );
2080 #if defined(HAVE_FRIBIDI)
2083 free( pi_karaoke_bar );
2084 return VLC_EGENERIC;
2086 p_sys->i_use_kerning =
2087 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2090 uint32_t *psz_unicode = (uint32_t *)
2091 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2094 if( p_face ) FT_Done_Face( p_face );
2095 free( pp_char_styles );
2096 free( psz_unicode );
2097 #if defined(HAVE_FRIBIDI)
2100 free( pi_karaoke_bar );
2103 memcpy( psz_unicode, psz_text + i_prev,
2104 sizeof( uint32_t ) * ( k - i_prev ) );
2105 psz_unicode[ k - i_prev ] = 0;
2106 while( *psz_unicode )
2110 if( !(p_line = NewLine( i_len - i_prev)) )
2112 if( p_face ) FT_Done_Face( p_face );
2113 free( pp_char_styles );
2114 free( psz_unicode );
2115 #if defined(HAVE_FRIBIDI)
2118 free( pi_karaoke_bar );
2121 /* New Color mode only works in YUVA rendering mode --
2122 * (RGB mode has palette constraints on it). We therefore
2123 * need to populate the legacy colour fields also.
2125 p_line->b_new_color_mode = true;
2126 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2127 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2128 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2129 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2130 p_line->p_next = NULL;
2132 i_pen_y += tmp_result.y;
2136 if( p_prev ) p_prev->p_next = p_line;
2137 else *pp_lines = p_line;
2140 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2141 p_style->i_font_color, p_style->b_underline,
2142 p_style->i_karaoke_bg_color,
2143 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2144 &tmp_result ) != VLC_SUCCESS )
2146 if( p_face ) FT_Done_Face( p_face );
2147 free( pp_char_styles );
2148 free( psz_unicode );
2149 #if defined(HAVE_FRIBIDI)
2152 free( pi_karaoke_bar );
2153 return VLC_EGENERIC;
2158 p_result->x = __MAX( p_result->x, tmp_result.x );
2159 p_result->y += tmp_result.y;
2164 if( *psz_unicode == '\n')
2168 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2170 *c_ptr = *(c_ptr+1);
2175 free( psz_unicode );
2176 if( p_face ) FT_Done_Face( p_face );
2180 free( pp_char_styles );
2181 #if defined(HAVE_FRIBIDI)
2186 p_result->x = __MAX( p_result->x, tmp_result.x );
2187 p_result->y += tmp_result.y;
2190 if( pi_karaoke_bar )
2193 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2195 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2197 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2201 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2203 /* 100% BG colour will render faster if we
2204 * instead make it 100% FG colour, so leave
2205 * the ratio alone and copy the value across
2207 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2211 if( pi_karaoke_bar[ i ] & 0x80 )
2213 /* Swap Left and Right sides over for Right aligned
2214 * language text (eg. Arabic, Hebrew)
2216 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2218 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2219 p_line->p_bg_rgb[ k ] = i_tmp;
2221 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2224 /* Jump over the '\n' at the line-end */
2227 free( pi_karaoke_bar );
2233 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2234 subpicture_region_t *p_region_in )
2236 int rv = VLC_SUCCESS;
2237 stream_t *p_sub = NULL;
2238 xml_t *p_xml = NULL;
2239 xml_reader_t *p_xml_reader = NULL;
2241 if( !p_region_in || !p_region_in->psz_html )
2242 return VLC_EGENERIC;
2244 /* Reset the default fontsize in case screen metrics have changed */
2245 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2247 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2248 (uint8_t *) p_region_in->psz_html,
2249 strlen( p_region_in->psz_html ),
2253 p_xml = xml_Create( p_filter );
2256 bool b_karaoke = false;
2258 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2261 /* Look for Root Node */
2262 if( xml_ReaderRead( p_xml_reader ) == 1 )
2264 char *psz_node = xml_ReaderName( p_xml_reader );
2266 if( !strcasecmp( "karaoke", psz_node ) )
2268 /* We're going to have to render the text a number
2269 * of times to show the progress marker on the text.
2271 var_SetBool( p_filter, "text-rerender", true );
2274 else if( !strcasecmp( "text", psz_node ) )
2280 /* Only text and karaoke tags are supported */
2281 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2282 xml_ReaderDelete( p_xml, p_xml_reader );
2283 p_xml_reader = NULL;
2295 uint32_t i_runs = 0;
2296 uint32_t i_k_runs = 0;
2297 uint32_t *pi_run_lengths = NULL;
2298 uint32_t *pi_k_run_lengths = NULL;
2299 uint32_t *pi_k_durations = NULL;
2300 ft_style_t **pp_styles = NULL;
2302 line_desc_t *p_lines = NULL;
2304 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2305 sizeof( uint32_t ) );
2310 rv = ProcessNodes( p_filter, p_xml_reader,
2311 p_region_in->p_style, psz_text, &i_len,
2312 &i_runs, &pi_run_lengths, &pp_styles,
2314 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2317 p_region_out->i_x = p_region_in->i_x;
2318 p_region_out->i_y = p_region_in->i_y;
2320 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2322 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2323 pi_run_lengths, pp_styles, &p_lines, &result,
2324 b_karaoke, i_k_runs, pi_k_run_lengths,
2328 for( k=0; k<i_runs; k++)
2329 DeleteStyle( pp_styles[k] );
2331 free( pi_run_lengths );
2334 /* Don't attempt to render text that couldn't be layed out
2337 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2339 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2341 Render( p_filter, p_region_out, p_lines,
2342 result.x, result.y );
2346 RenderYUVA( p_filter, p_region_out, p_lines,
2347 result.x, result.y );
2351 FreeLines( p_lines );
2353 xml_ReaderDelete( p_xml, p_xml_reader );
2355 xml_Delete( p_xml );
2357 stream_Delete( p_sub );
2363 static char* FontConfig_Select( FcConfig* priv, const char* family,
2364 bool b_bold, bool b_italic, int *i_idx )
2367 FcPattern *pat, *p_pat;
2371 pat = FcPatternCreate();
2372 if (!pat) return NULL;
2374 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2375 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2376 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2377 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2379 FcDefaultSubstitute( pat );
2381 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2383 FcPatternDestroy( pat );
2387 p_pat = FcFontMatch( priv, pat, &result );
2388 FcPatternDestroy( pat );
2389 if( !p_pat ) return NULL;
2391 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2392 || ( val_b != FcTrue ) )
2394 FcPatternDestroy( p_pat );
2397 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2402 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2404 FcPatternDestroy( p_pat );
2409 if( strcasecmp((const char*)val_s, family ) != 0 )
2410 msg_Warn( p_filter, "fontconfig: selected font family is not"
2411 "the requested one: '%s' != '%s'\n",
2412 (const char*)val_s, family );
2415 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2417 FcPatternDestroy( p_pat );
2421 FcPatternDestroy( p_pat );
2422 return strdup( (const char*)val_s );
2426 static void FreeLine( line_desc_t *p_line )
2429 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2431 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2433 free( p_line->pp_glyphs );
2434 free( p_line->p_glyph_pos );
2435 free( p_line->p_fg_rgb );
2436 free( p_line->p_bg_rgb );
2437 free( p_line->p_fg_bg_ratio );
2438 free( p_line->pi_underline_offset );
2439 free( p_line->pi_underline_thickness );
2443 static void FreeLines( line_desc_t *p_lines )
2445 line_desc_t *p_line, *p_next;
2447 if( !p_lines ) return;
2449 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2451 p_next = p_line->p_next;
2456 static line_desc_t *NewLine( int i_count )
2458 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2460 if( !p_line ) return NULL;
2461 p_line->i_height = 0;
2462 p_line->i_width = 0;
2463 p_line->p_next = NULL;
2465 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2466 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2467 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2468 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2469 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2470 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2471 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2472 if( ( p_line->pp_glyphs == NULL ) ||
2473 ( p_line->p_glyph_pos == NULL ) ||
2474 ( p_line->p_fg_rgb == NULL ) ||
2475 ( p_line->p_bg_rgb == NULL ) ||
2476 ( p_line->p_fg_bg_ratio == NULL ) ||
2477 ( p_line->pi_underline_offset == NULL ) ||
2478 ( p_line->pi_underline_thickness == NULL ) )
2480 free( p_line->pi_underline_thickness );
2481 free( p_line->pi_underline_offset );
2482 free( p_line->p_fg_rgb );
2483 free( p_line->p_bg_rgb );
2484 free( p_line->p_fg_bg_ratio );
2485 free( p_line->p_glyph_pos );
2486 free( p_line->pp_glyphs );
2490 p_line->pp_glyphs[0] = NULL;
2491 p_line->b_new_color_mode = false;
2496 static int GetFontSize( filter_t *p_filter )
2498 filter_sys_t *p_sys = p_filter->p_sys;
2502 if( p_sys->i_default_font_size )
2504 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2505 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2507 i_size = p_sys->i_default_font_size;
2511 var_Get( p_filter, "freetype-rel-fontsize", &val );
2514 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2515 p_filter->p_sys->i_display_height =
2516 p_filter->fmt_out.video.i_height;
2521 msg_Warn( p_filter, "invalid fontsize, using 12" );
2522 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2523 i_size = 12 * val.i_int / 1000;
2530 static int SetFontSize( filter_t *p_filter, int i_size )
2532 filter_sys_t *p_sys = p_filter->p_sys;
2536 i_size = GetFontSize( p_filter );
2538 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2541 p_sys->i_font_size = i_size;
2543 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2545 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2546 return VLC_EGENERIC;
2552 static void YUVFromRGB( uint32_t i_argb,
2553 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2555 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2556 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2557 int i_blue = ( i_argb & 0x000000ff );
2559 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2560 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2561 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2562 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2563 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2564 -585 * i_blue + 4096 + 1048576) >> 13, 240);