1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
47 #include FT_FREETYPE_H
49 #define FT_FLOOR(X) ((X & -64) >> 6)
50 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
51 #define FT_MulFix(v, s) (((v)*(s))>>16)
54 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
55 #define FC_DEFAULT_FONT "Lucida Grande"
56 #elif defined( SYS_BEOS )
57 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
58 #define FC_DEFAULT_FONT "Swiss"
59 #elif defined( WIN32 )
60 #define DEFAULT_FONT "" /* Default font found at run-time */
61 #define FC_DEFAULT_FONT "Arial"
63 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
64 #define FC_DEFAULT_FONT "Serif Bold"
67 #if defined(HAVE_FRIBIDI)
68 #include <fribidi/fribidi.h>
71 #ifdef HAVE_FONTCONFIG
72 #include <fontconfig/fontconfig.h>
77 /*****************************************************************************
79 *****************************************************************************/
80 static int Create ( vlc_object_t * );
81 static void Destroy( vlc_object_t * );
83 #define FONT_TEXT N_("Font")
84 #define FONT_LONGTEXT N_("Filename for the font you want to use")
85 #define FONTSIZE_TEXT N_("Font size in pixels")
86 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
87 "that will be rendered on the video. " \
88 "If set to something different than 0 this option will override the " \
89 "relative font size." )
90 #define OPACITY_TEXT N_("Opacity")
91 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
92 "text that will be rendered on the video. 0 = transparent, " \
93 "255 = totally opaque. " )
94 #define COLOR_TEXT N_("Text default color")
95 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
96 "the video. This must be an hexadecimal (like HTML colors). The first two "\
97 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
98 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
99 #define FONTSIZER_TEXT N_("Relative font size")
100 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
101 "fonts that will be rendered on the video. If absolute font size is set, "\
102 "relative size will be overriden." )
104 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
105 static const char *const ppsz_sizes_text[] = {
106 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
107 #define YUVP_TEXT N_("Use YUVP renderer")
108 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
109 "This option is only needed if you want to encode into DVB subtitles" )
110 #define EFFECT_TEXT N_("Font Effect")
111 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
112 "text to improve its readability." )
114 #define EFFECT_BACKGROUND 1
115 #define EFFECT_OUTLINE 2
116 #define EFFECT_OUTLINE_FAT 3
118 static int const pi_effects[] = { 1, 2, 3 };
119 static const char *const ppsz_effects_text[] = {
120 N_("Background"),N_("Outline"), N_("Fat Outline") };
121 static const int pi_color_values[] = {
122 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
123 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
124 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
126 static const char *const ppsz_color_descriptions[] = {
127 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
128 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
129 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
132 set_shortname( N_("Text renderer"))
133 set_description( N_("Freetype2 font renderer") )
134 set_category( CAT_VIDEO )
135 set_subcategory( SUBCAT_VIDEO_SUBPIC )
137 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
140 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
141 FONTSIZE_LONGTEXT, true );
143 /* opacity valid on 0..255, with default 255 = fully opaque */
144 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
145 OPACITY_TEXT, OPACITY_LONGTEXT, true );
147 /* hook to the color values list, with default 0x00ffffff = white */
148 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
149 COLOR_LONGTEXT, false );
150 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
152 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
153 FONTSIZER_LONGTEXT, false );
154 change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
155 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
156 EFFECT_LONGTEXT, false );
157 change_integer_list( pi_effects, ppsz_effects_text, NULL );
159 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
160 YUVP_LONGTEXT, true );
161 set_capability( "text renderer", 100 )
162 add_shortcut( "text" )
163 set_callbacks( Create, Destroy )
168 /*****************************************************************************
170 *****************************************************************************/
172 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
173 static int RenderText( filter_t *, subpicture_region_t *,
174 subpicture_region_t * );
175 #ifdef HAVE_FONTCONFIG
176 static int RenderHtml( filter_t *, subpicture_region_t *,
177 subpicture_region_t * );
178 static char *FontConfig_Select( FcConfig *, const char *,
183 static int LoadFontsFromAttachments( filter_t *p_filter );
185 static int GetFontSize( filter_t *p_filter );
186 static int SetFontSize( filter_t *, int );
187 static void YUVFromRGB( uint32_t i_argb,
188 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
190 typedef struct line_desc_t line_desc_t;
193 /** NULL-terminated list of glyphs making the string */
194 FT_BitmapGlyph *pp_glyphs;
195 /** list of relative positions for the glyphs */
196 FT_Vector *p_glyph_pos;
197 /** list of RGB information for styled text
198 * -- if the rendering mode supports it (RenderYUVA) and
199 * b_new_color_mode is set, then it becomes possible to
200 * have multicoloured text within the subtitles. */
203 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
204 bool b_new_color_mode;
205 /** underline information -- only supplied if text should be underlined */
206 uint16_t *pi_underline_offset;
207 uint16_t *pi_underline_thickness;
211 int i_red, i_green, i_blue;
216 static line_desc_t *NewLine( int );
221 uint32_t i_font_color; /* ARGB */
222 uint32_t i_karaoke_bg_color; /* ARGB */
229 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
230 static void FreeLines( line_desc_t * );
231 static void FreeLine( line_desc_t * );
233 #ifdef HAVE_FONTCONFIG
234 static vlc_object_t *FontBuilderAttach( filter_t *p_filter );
235 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
236 static void* FontBuilderThread( vlc_object_t *p_this);
237 static void FontBuilderDestructor( vlc_object_t *p_this );
238 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
239 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
243 /*****************************************************************************
244 * filter_sys_t: freetype local data
245 *****************************************************************************
246 * This structure is part of the video output thread descriptor.
247 * It describes the freetype specific properties of an output thread.
248 *****************************************************************************/
251 FT_Library p_library; /* handle to library */
252 FT_Face p_face; /* handle to face object */
254 uint8_t i_font_opacity;
259 int i_default_font_size;
260 int i_display_height;
261 #ifdef HAVE_FONTCONFIG
262 bool b_fontconfig_ok;
263 FcConfig *p_fontconfig;
266 input_attachment_t **pp_font_attachments;
267 int i_font_attachments;
269 vlc_object_t *p_fontbuilder;
272 #define UCHAR uint32_t
273 #define TR_DEFAULT_FONT FC_DEFAULT_FONT
274 #define TR_FONT_STYLE_PTR ft_style_t *
276 #include "text_renderer.h"
278 /*****************************************************************************
279 * Create: allocates osd-text video thread output method
280 *****************************************************************************
281 * This function allocates and initializes a Clone vout method.
282 *****************************************************************************/
283 static int Create( vlc_object_t *p_this )
285 filter_t *p_filter = (filter_t *)p_this;
287 char *psz_fontfile = NULL;
291 /* Allocate structure */
292 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
296 p_sys->p_library = 0;
297 p_sys->i_font_size = 0;
298 p_sys->i_display_height = 0;
300 var_Create( p_filter, "freetype-font",
301 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
302 var_Create( p_filter, "freetype-fontsize",
303 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
304 var_Create( p_filter, "freetype-rel-fontsize",
305 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
306 var_Create( p_filter, "freetype-opacity",
307 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
308 var_Create( p_filter, "freetype-effect",
309 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
310 var_Get( p_filter, "freetype-opacity", &val );
311 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
312 var_Create( p_filter, "freetype-color",
313 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
314 var_Get( p_filter, "freetype-color", &val );
315 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
316 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
318 /* Look what method was requested */
319 var_Get( p_filter, "freetype-font", &val );
320 psz_fontfile = val.psz_string;
321 if( !psz_fontfile || !*psz_fontfile )
323 free( psz_fontfile );
324 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
328 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
329 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
330 #elif defined(__APPLE__)
331 strcpy( psz_fontfile, DEFAULT_FONT );
333 msg_Err( p_filter, "user didn't specify a font" );
338 i_error = FT_Init_FreeType( &p_sys->p_library );
341 msg_Err( p_filter, "couldn't initialize freetype" );
344 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
346 if( i_error == FT_Err_Unknown_File_Format )
348 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
353 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
357 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
360 msg_Err( p_filter, "font has no unicode translation table" );
364 #ifdef HAVE_FONTCONFIG
365 p_sys->b_fontconfig_ok = false;
366 p_sys->p_fontconfig = NULL;
367 p_sys->p_fontbuilder = FontBuilderAttach( p_filter );
370 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
372 var_Get( p_filter, "freetype-fontsize", &val );
373 p_sys->i_default_font_size = val.i_int;
374 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
376 free( psz_fontfile );
378 p_sys->pp_font_attachments = NULL;
379 p_sys->i_font_attachments = 0;
381 p_filter->pf_render_text = RenderText;
382 #ifdef HAVE_FONTCONFIG
383 p_filter->pf_render_html = RenderHtml;
385 p_filter->pf_render_html = NULL;
388 LoadFontsFromAttachments( p_filter );
393 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
394 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
395 free( psz_fontfile );
400 /*****************************************************************************
401 * Destroy: destroy Clone video thread output method
402 *****************************************************************************
403 * Clean up all data and library connections
404 *****************************************************************************/
405 static void Destroy( vlc_object_t *p_this )
407 filter_t *p_filter = (filter_t *)p_this;
408 filter_sys_t *p_sys = p_filter->p_sys;
410 if( p_sys->pp_font_attachments )
414 for( k = 0; k < p_sys->i_font_attachments; k++ )
415 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
417 free( p_sys->pp_font_attachments );
420 #ifdef HAVE_FONTCONFIG
421 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
424 /* FcFini asserts calling the subfunction FcCacheFini()
425 * even if no other library functions have been made since FcInit(),
426 * so don't call it. */
428 FT_Done_Face( p_sys->p_face );
429 FT_Done_FreeType( p_sys->p_library );
433 #ifdef HAVE_FONTCONFIG
434 static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
436 static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
438 /* Check for an existing Fontbuilder thread */
439 vlc_mutex_lock( &fb_lock );
440 vlc_object_t *p_fontbuilder =
441 vlc_object_find_name( p_filter->p_libvlc,
442 "fontlist builder", FIND_CHILD );
446 /* Create the FontBuilderThread thread as a child of a top-level
447 * object, so that it can survive the destruction of the
448 * freetype object - the fontlist only needs to be built once,
449 * and calling the fontbuild a second time while the first is
450 * still in progress can cause thread instabilities.
452 * XXX The fontbuilder will be destroy as soon as it is unused.
455 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
456 sizeof(vlc_object_t) );
459 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
460 p_fontbuilder->p_private = NULL;
461 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
463 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
465 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
466 var_SetBool( p_fontbuilder, "build-done", false );
468 if( vlc_thread_create( p_fontbuilder,
471 VLC_THREAD_PRIORITY_LOW,
474 msg_Warn( p_filter, "fontconfig database builder thread can't "
475 "be launched. Font styling support will be limited." );
481 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
482 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
484 vlc_mutex_unlock( &fb_lock );
485 return p_fontbuilder;
487 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
489 vlc_mutex_lock( &fb_lock );
492 const bool b_alive = vlc_object_alive( p_fontbuilder );
494 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
496 /* We wait for the thread on the first FontBuilderDetach */
499 vlc_object_kill( p_fontbuilder );
500 vlc_mutex_unlock( &fb_lock );
502 /* We need to unlock otherwise we may not join (the thread waiting
503 * for the lock). It is safe to unlock as no one else will try a
504 * join and we have a reference on the object) */
505 vlc_thread_join( p_fontbuilder );
507 vlc_mutex_lock( &fb_lock );
509 vlc_object_release( p_fontbuilder );
511 vlc_mutex_unlock( &fb_lock );
513 static void* FontBuilderThread( vlc_object_t *p_this )
515 FcConfig *p_fontconfig = FcInitLoadConfig();
520 int canc = vlc_savecancel ();
522 //msg_Dbg( p_this, "Building font database..." );
523 msg_Dbg( p_this, "Building font database..." );
525 if(! FcConfigBuildFonts( p_fontconfig ))
527 /* Don't destroy the fontconfig object - we won't be able to do
528 * italics or bold or change the font face, but we will still
529 * be able to do underline and change the font size.
531 msg_Err( p_this, "fontconfig database can't be built. "
532 "Font styling won't be available" );
536 msg_Dbg( p_this, "Finished building font database." );
537 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
539 vlc_mutex_lock( &fb_lock );
540 p_this->p_private = p_fontconfig;
541 vlc_mutex_unlock( &fb_lock );
543 var_SetBool( p_this, "build-done", true );
544 vlc_restorecancel (canc);
548 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
550 filter_sys_t *p_sys = p_filter->p_sys;
552 p_sys->p_fontconfig = p_fontbuilder->p_private;
553 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
555 static void FontBuilderDestructor( vlc_object_t *p_this )
557 FcConfig *p_fontconfig = p_this->p_private;
560 FcConfigDestroy( p_fontconfig );
562 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
563 vlc_value_t oldval, vlc_value_t newval, void *param )
565 filter_t *p_filter = param;
569 vlc_mutex_lock( &fb_lock );
571 FontBuilderGetFcConfig( p_filter, p_this );
573 vlc_mutex_unlock( &fb_lock );
582 /*****************************************************************************
583 * Make any TTF/OTF fonts present in the attachments of the media file
584 * and store them for later use by the FreeType Engine
585 *****************************************************************************/
586 static int LoadFontsFromAttachments( filter_t *p_filter )
588 filter_sys_t *p_sys = p_filter->p_sys;
589 input_thread_t *p_input;
590 input_attachment_t **pp_attachments;
591 int i_attachments_cnt;
593 int rv = VLC_SUCCESS;
595 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
599 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
601 vlc_object_release(p_input);
605 p_sys->i_font_attachments = 0;
606 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
607 if(! p_sys->pp_font_attachments )
610 for( k = 0; k < i_attachments_cnt; k++ )
612 input_attachment_t *p_attach = pp_attachments[k];
614 if( p_sys->pp_font_attachments )
616 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
617 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
618 ( p_attach->i_data > 0 ) &&
619 ( p_attach->p_data != NULL ) )
621 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
625 vlc_input_attachment_Delete( p_attach );
630 vlc_input_attachment_Delete( p_attach );
633 free( pp_attachments );
635 vlc_object_release(p_input);
640 /*****************************************************************************
641 * Render: place string in picture
642 *****************************************************************************
643 * This function merges the previously rendered freetype glyphs into a picture
644 *****************************************************************************/
645 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
646 line_desc_t *p_line, int i_width, int i_height )
648 static const uint8_t pi_gamma[16] =
649 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
650 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
654 int i, x, y, i_pitch;
655 uint8_t i_y; /* YUV values, derived from incoming RGB */
658 /* Create a new subpicture region */
659 memset( &fmt, 0, sizeof(video_format_t) );
660 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
662 fmt.i_width = fmt.i_visible_width = i_width + 4;
663 fmt.i_height = fmt.i_visible_height = i_height + 4;
664 if( p_region->fmt.i_visible_width > 0 )
665 fmt.i_visible_width = p_region->fmt.i_visible_width;
666 if( p_region->fmt.i_visible_height > 0 )
667 fmt.i_visible_height = p_region->fmt.i_visible_height;
668 fmt.i_x_offset = fmt.i_y_offset = 0;
670 assert( !p_region->p_picture );
671 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
672 if( !p_region->p_picture )
676 /* Calculate text color components */
677 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
678 25 * p_line->i_blue + 128) >> 8) + 16;
679 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
680 112 * p_line->i_blue + 128) >> 8) + 128;
681 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
682 18 * p_line->i_blue + 128) >> 8) + 128;
685 fmt.p_palette->i_entries = 16;
686 for( i = 0; i < 8; i++ )
688 fmt.p_palette->palette[i][0] = 0;
689 fmt.p_palette->palette[i][1] = 0x80;
690 fmt.p_palette->palette[i][2] = 0x80;
691 fmt.p_palette->palette[i][3] = pi_gamma[i];
692 fmt.p_palette->palette[i][3] =
693 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
695 for( i = 8; i < fmt.p_palette->i_entries; i++ )
697 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
698 fmt.p_palette->palette[i][1] = i_u;
699 fmt.p_palette->palette[i][2] = i_v;
700 fmt.p_palette->palette[i][3] = pi_gamma[i];
701 fmt.p_palette->palette[i][3] =
702 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
705 p_dst = p_region->p_picture->Y_PIXELS;
706 i_pitch = p_region->p_picture->Y_PITCH;
708 /* Initialize the region pixels */
709 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
711 for( ; p_line != NULL; p_line = p_line->p_next )
713 int i_glyph_tmax = 0;
714 int i_bitmap_offset, i_offset, i_align_offset = 0;
715 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
717 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
718 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
721 if( p_line->i_width < i_width )
723 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
725 i_align_offset = i_width - p_line->i_width;
727 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
729 i_align_offset = ( i_width - p_line->i_width ) / 2;
733 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
735 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
737 i_offset = ( p_line->p_glyph_pos[ i ].y +
738 i_glyph_tmax - p_glyph->top + 2 ) *
739 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
742 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
744 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
746 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
748 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
755 /* Outlining (find something better than nearest neighbour filtering ?) */
758 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
759 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
760 uint8_t left, current;
762 for( y = 1; y < (int)fmt.i_height - 1; y++ )
764 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
765 p_dst += p_region->p_picture->Y_PITCH;
768 for( x = 1; x < (int)fmt.i_width - 1; x++ )
771 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
772 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;
776 memset( p_top, 0, fmt.i_width );
782 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
783 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
784 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
785 int i_glyph_tmax, int i_align_offset,
786 uint8_t i_y, uint8_t i_u, uint8_t i_v,
787 subpicture_region_t *p_region)
791 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
793 p_dst_y = p_region->p_picture->Y_PIXELS;
794 p_dst_u = p_region->p_picture->U_PIXELS;
795 p_dst_v = p_region->p_picture->V_PIXELS;
796 p_dst_a = p_region->p_picture->A_PIXELS;
797 i_pitch = p_region->p_picture->A_PITCH;
799 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
800 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
802 for( y = 0; y < i_line_thickness; y++ )
804 int i_extra = p_this_glyph->bitmap.width;
808 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
809 (p_this_glyph_pos->x + p_this_glyph->left);
811 for( x = 0; x < i_extra; x++ )
815 /* break the underline around the tails of any glyphs which cross it */
816 for( z = x - i_line_thickness;
817 z < x + i_line_thickness && b_ok;
820 if( p_next_glyph && ( z >= i_extra ) )
822 int i_row = i_line_offset + p_next_glyph->top + y;
824 if( ( p_next_glyph->bitmap.rows > i_row ) &&
825 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
830 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
832 int i_row = i_line_offset + p_this_glyph->top + y;
834 if( ( p_this_glyph->bitmap.rows > i_row ) &&
835 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
844 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
845 p_dst_u[i_offset+x] = i_u;
846 p_dst_v[i_offset+x] = i_v;
847 p_dst_a[i_offset+x] = 255;
854 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
856 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
857 int i_pitch = p_region->p_picture->A_PITCH;
860 for( ; p_line != NULL; p_line = p_line->p_next )
862 int i_glyph_tmax=0, i = 0;
863 int i_bitmap_offset, i_offset, i_align_offset = 0;
864 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
866 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
867 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
870 if( p_line->i_width < i_width )
872 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
874 i_align_offset = i_width - p_line->i_width;
876 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
878 i_align_offset = ( i_width - p_line->i_width ) / 2;
882 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
884 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
886 i_offset = ( p_line->p_glyph_pos[ i ].y +
887 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
888 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
889 i_align_offset +xoffset;
891 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
893 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
895 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
896 if( p_dst[i_offset+x] <
897 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
899 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
908 /*****************************************************************************
909 * Render: place string in picture
910 *****************************************************************************
911 * This function merges the previously rendered freetype glyphs into a picture
912 *****************************************************************************/
913 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
914 line_desc_t *p_line, int i_width, int i_height )
916 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
918 int i, x, y, i_pitch, i_alpha;
919 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
921 if( i_width == 0 || i_height == 0 )
924 /* Create a new subpicture region */
925 memset( &fmt, 0, sizeof(video_format_t) );
926 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
928 fmt.i_width = fmt.i_visible_width = i_width + 6;
929 fmt.i_height = fmt.i_visible_height = i_height + 6;
930 if( p_region->fmt.i_visible_width > 0 )
931 fmt.i_visible_width = p_region->fmt.i_visible_width;
932 if( p_region->fmt.i_visible_height > 0 )
933 fmt.i_visible_height = p_region->fmt.i_visible_height;
934 fmt.i_x_offset = fmt.i_y_offset = 0;
936 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
937 if( !p_region->p_picture )
941 /* Calculate text color components */
942 YUVFromRGB( (p_line->i_red << 16) |
943 (p_line->i_green << 8) |
946 i_alpha = p_line->i_alpha;
948 p_dst_y = p_region->p_picture->Y_PIXELS;
949 p_dst_u = p_region->p_picture->U_PIXELS;
950 p_dst_v = p_region->p_picture->V_PIXELS;
951 p_dst_a = p_region->p_picture->A_PIXELS;
952 i_pitch = p_region->p_picture->A_PITCH;
954 /* Initialize the region pixels */
955 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
957 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
958 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
959 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
960 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
964 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
965 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
966 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
967 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
969 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
970 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
972 DrawBlack( p_line, i_width, p_region, 0, 0);
973 DrawBlack( p_line, i_width, p_region, -1, 0);
974 DrawBlack( p_line, i_width, p_region, 0, -1);
975 DrawBlack( p_line, i_width, p_region, 1, 0);
976 DrawBlack( p_line, i_width, p_region, 0, 1);
979 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
984 DrawBlack( p_line, i_width, p_region, 1, 1);
986 DrawBlack( p_line, i_width, p_region, -2, 0);
987 DrawBlack( p_line, i_width, p_region, 0, -2);
988 DrawBlack( p_line, i_width, p_region, 2, 0);
989 DrawBlack( p_line, i_width, p_region, 0, 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);
994 DrawBlack( p_line, i_width, p_region, 2, 2);
996 DrawBlack( p_line, i_width, p_region, -3, 0);
997 DrawBlack( p_line, i_width, p_region, 0, -3);
998 DrawBlack( p_line, i_width, p_region, 3, 0);
999 DrawBlack( p_line, i_width, p_region, 0, 3);
1002 for( ; p_line != NULL; p_line = p_line->p_next )
1004 int i_glyph_tmax = 0;
1005 int i_bitmap_offset, i_offset, i_align_offset = 0;
1006 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1008 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1009 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1012 if( p_line->i_width < i_width )
1014 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1016 i_align_offset = i_width - p_line->i_width;
1018 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1020 i_align_offset = ( i_width - p_line->i_width ) / 2;
1024 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1026 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1028 i_offset = ( p_line->p_glyph_pos[ i ].y +
1029 i_glyph_tmax - p_glyph->top + 3 ) *
1030 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1033 if( p_line->b_new_color_mode )
1035 /* Every glyph can (and in fact must) have its own color */
1036 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1039 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1041 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1043 uint8_t i_y_local = i_y;
1044 uint8_t i_u_local = i_u;
1045 uint8_t i_v_local = i_v;
1047 if( p_line->p_fg_bg_ratio != 0x00 )
1049 int i_split = p_glyph->bitmap.width *
1050 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1054 YUVFromRGB( p_line->p_bg_rgb[ i ],
1055 &i_y_local, &i_u_local, &i_v_local );
1059 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1061 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1062 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1064 p_dst_u[i_offset+x] = i_u;
1065 p_dst_v[i_offset+x] = i_v;
1067 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1068 p_dst_a[i_offset+x] = 0xff;
1071 i_offset += i_pitch;
1074 if( p_line->pi_underline_thickness[ i ] )
1076 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1077 p_line->pi_underline_offset[ i ],
1078 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1079 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1080 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1081 i_glyph_tmax, i_align_offset,
1088 /* Apply the alpha setting */
1089 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1090 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1096 * This function renders a text subpicture region into another one.
1097 * It also calculates the size needed for this string, and renders the
1098 * needed glyphs into memory. It is used as pf_add_string callback in
1099 * the vout method by this module
1101 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1102 subpicture_region_t *p_region_in )
1104 filter_sys_t *p_sys = p_filter->p_sys;
1105 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1106 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1107 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1108 int i_string_length;
1110 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1111 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1121 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1122 psz_string = p_region_in->psz_text;
1123 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1125 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1126 i_scale = val.i_int;
1128 if( p_region_in->p_style )
1130 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1131 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1132 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1136 i_font_color = p_sys->i_font_color;
1137 i_font_alpha = 255 - p_sys->i_font_opacity;
1138 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1141 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1142 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1143 SetFontSize( p_filter, i_font_size );
1145 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1146 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1147 i_blue = i_font_color & 0x000000FF;
1149 result.x = result.y = 0;
1150 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1152 psz_unicode = psz_unicode_orig =
1153 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1154 if( psz_unicode == NULL )
1156 #if defined(WORDS_BIGENDIAN)
1157 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1159 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1161 if( iconv_handle == (vlc_iconv_t)-1 )
1163 msg_Warn( p_filter, "unable to do conversion" );
1169 const char *p_in_buffer = psz_string;
1170 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1171 i_in_bytes = strlen( psz_string );
1172 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1173 i_out_bytes_left = i_out_bytes;
1174 p_out_buffer = (char *)psz_unicode;
1175 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1177 &p_out_buffer, &i_out_bytes_left );
1179 vlc_iconv_close( iconv_handle );
1183 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1184 "bytes left %u", (unsigned)i_in_bytes );
1187 *(uint32_t*)p_out_buffer = 0;
1188 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1191 #if defined(HAVE_FRIBIDI)
1193 uint32_t *p_fribidi_string;
1194 int32_t start_pos, pos = 0;
1196 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1197 if( !p_fribidi_string )
1200 /* Do bidi conversion line-by-line */
1201 while( pos < i_string_length )
1203 while( pos < i_string_length )
1205 i_char = psz_unicode[pos];
1206 if (i_char != '\r' && i_char != '\n')
1208 p_fribidi_string[pos] = i_char;
1212 while( pos < i_string_length )
1214 i_char = psz_unicode[pos];
1215 if (i_char == '\r' || i_char == '\n')
1219 if (pos > start_pos)
1221 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1222 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1225 (FriBidiChar*)p_fribidi_string + start_pos,
1230 free( psz_unicode_orig );
1231 psz_unicode = psz_unicode_orig = p_fribidi_string;
1232 p_fribidi_string[ i_string_length ] = 0;
1236 /* Calculate relative glyph positions and a bounding box for the
1238 if( !(p_line = NewLine( strlen( psz_string ))) )
1241 i_pen_x = i_pen_y = 0;
1243 psz_line_start = psz_unicode;
1245 #define face p_sys->p_face
1246 #define glyph face->glyph
1248 while( *psz_unicode )
1250 i_char = *psz_unicode++;
1251 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1256 if( i_char == '\n' )
1258 psz_line_start = psz_unicode;
1259 if( !(p_next = NewLine( strlen( psz_string ))) )
1261 p_line->p_next = p_next;
1262 p_line->i_width = line.xMax;
1263 p_line->i_height = face->size->metrics.height >> 6;
1264 p_line->pp_glyphs[ i ] = NULL;
1265 p_line->i_alpha = i_font_alpha;
1266 p_line->i_red = i_red;
1267 p_line->i_green = i_green;
1268 p_line->i_blue = i_blue;
1271 result.x = __MAX( result.x, line.xMax );
1272 result.y += face->size->metrics.height >> 6;
1275 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1276 i_pen_y += face->size->metrics.height >> 6;
1278 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1283 i_glyph_index = FT_Get_Char_Index( face, i_char );
1284 if( p_sys->i_use_kerning && i_glyph_index
1288 FT_Get_Kerning( face, i_previous, i_glyph_index,
1289 ft_kerning_default, &delta );
1290 i_pen_x += delta.x >> 6;
1293 p_line->p_glyph_pos[ i ].x = i_pen_x;
1294 p_line->p_glyph_pos[ i ].y = i_pen_y;
1295 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1298 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1302 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1305 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1309 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1310 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1313 FT_Done_Glyph( tmp_glyph );
1316 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1319 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1320 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1321 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1323 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1324 p_line->pp_glyphs[ i ] = NULL;
1326 p_line = NewLine( strlen( psz_string ));
1327 if( p_prev ) p_prev->p_next = p_line;
1328 else p_lines = p_line;
1330 uint32_t *psz_unicode_saved = psz_unicode;
1331 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1335 if( psz_unicode == psz_line_start )
1336 { /* try harder to break that line */
1337 psz_unicode = psz_unicode_saved;
1338 while( psz_unicode > psz_line_start &&
1339 *psz_unicode != '_' && *psz_unicode != '/' &&
1340 *psz_unicode != '\\' && *psz_unicode != '.' )
1345 if( psz_unicode == psz_line_start )
1347 msg_Warn( p_filter, "unbreakable string" );
1352 *psz_unicode = '\n';
1354 psz_unicode = psz_line_start;
1357 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1360 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1361 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1363 i_previous = i_glyph_index;
1364 i_pen_x += glyph->advance.x >> 6;
1368 p_line->i_width = line.xMax;
1369 p_line->i_height = face->size->metrics.height >> 6;
1370 p_line->pp_glyphs[ i ] = NULL;
1371 p_line->i_alpha = i_font_alpha;
1372 p_line->i_red = i_red;
1373 p_line->i_green = i_green;
1374 p_line->i_blue = i_blue;
1375 result.x = __MAX( result.x, line.xMax );
1376 result.y += line.yMax - line.yMin;
1381 p_region_out->i_x = p_region_in->i_x;
1382 p_region_out->i_y = p_region_in->i_y;
1384 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1385 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1387 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1389 free( psz_unicode_orig );
1390 FreeLines( p_lines );
1394 free( psz_unicode_orig );
1395 FreeLines( p_lines );
1396 return VLC_EGENERIC;
1399 #ifdef HAVE_FONTCONFIG
1400 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1401 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1402 bool b_italic, bool b_uline )
1404 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1408 p_style->i_font_size = i_font_size;
1409 p_style->i_font_color = i_font_color;
1410 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1411 p_style->b_italic = b_italic;
1412 p_style->b_bold = b_bold;
1413 p_style->b_underline = b_uline;
1415 p_style->psz_fontname = strdup( psz_fontname );
1420 static void DeleteStyle( ft_style_t *p_style )
1424 free( p_style->psz_fontname );
1429 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1436 if(( s1->i_font_size == s2->i_font_size ) &&
1437 ( s1->i_font_color == s2->i_font_color ) &&
1438 ( s1->b_italic == s2->b_italic ) &&
1439 ( s1->b_bold == s2->b_bold ) &&
1440 ( s1->b_underline == s2->b_underline ) &&
1441 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1448 static void IconvText( filter_t *p_filter, const char *psz_string,
1449 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1451 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1453 /* If memory hasn't been allocated for our output string, allocate it here
1454 * - the calling function must now be responsible for freeing it.
1456 if( !*ppsz_unicode )
1457 *ppsz_unicode = (uint32_t *)
1458 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1460 /* We don't need to handle a NULL pointer in *ppsz_unicode
1461 * if we are instead testing for a non NULL value like we are here */
1465 #if defined(WORDS_BIGENDIAN)
1466 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1468 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1470 if( iconv_handle != (vlc_iconv_t)-1 )
1472 char *p_in_buffer, *p_out_buffer;
1473 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1474 i_in_bytes = strlen( psz_string );
1475 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1476 i_out_bytes_left = i_out_bytes;
1477 p_in_buffer = (char *) psz_string;
1478 p_out_buffer = (char *) *ppsz_unicode;
1479 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1480 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1482 vlc_iconv_close( iconv_handle );
1486 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1487 "bytes left %u", (unsigned)i_in_bytes );
1491 *(uint32_t*)p_out_buffer = 0;
1493 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1498 msg_Warn( p_filter, "unable to do conversion" );
1503 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1504 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1507 ft_style_t *p_style = NULL;
1509 char *psz_fontname = NULL;
1510 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1511 uint32_t i_karaoke_bg_color = i_font_color;
1512 int i_font_size = p_sys->i_font_size;
1514 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1515 &i_font_color, &i_karaoke_bg_color ))
1517 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1518 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1523 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1524 bool b_uline, int i_karaoke_bgcolor,
1525 line_desc_t *p_line, uint32_t *psz_unicode,
1526 int *pi_pen_x, int i_pen_y, int *pi_start,
1527 FT_Vector *p_result )
1532 bool b_first_on_line = true;
1535 int i_pen_x_start = *pi_pen_x;
1537 uint32_t *psz_unicode_start = psz_unicode;
1539 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1541 /* Account for part of line already in position */
1542 for( i=0; i<*pi_start; i++ )
1546 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1547 ft_glyph_bbox_pixels, &glyph_size );
1549 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1550 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1551 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1552 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1558 b_first_on_line = false;
1560 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1566 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1567 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1571 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1572 ft_kerning_default, &delta );
1573 *pi_pen_x += delta.x >> 6;
1575 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1576 p_line->p_glyph_pos[ i ].y = i_pen_y;
1578 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1582 "unable to render text FT_Load_Glyph returned %d", i_error );
1583 p_line->pp_glyphs[ i ] = NULL;
1584 return VLC_EGENERIC;
1586 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1590 "unable to render text FT_Get_Glyph returned %d", i_error );
1591 p_line->pp_glyphs[ i ] = NULL;
1592 return VLC_EGENERIC;
1594 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1595 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1598 FT_Done_Glyph( tmp_glyph );
1603 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1604 p_face->size->metrics.y_scale));
1605 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1606 p_face->size->metrics.y_scale));
1608 p_line->pi_underline_offset[ i ] =
1609 ( aOffset < 0 ) ? -aOffset : aOffset;
1610 p_line->pi_underline_thickness[ i ] =
1611 ( aSize < 0 ) ? -aSize : aSize;
1613 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1614 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1615 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1616 p_line->p_fg_bg_ratio[ i ] = 0x00;
1618 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1619 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1620 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1622 for( ; i >= *pi_start; i-- )
1623 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1626 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1630 if( psz_unicode == psz_unicode_start )
1632 if( b_first_on_line )
1634 msg_Warn( p_filter, "unbreakable string" );
1635 p_line->pp_glyphs[ i ] = NULL;
1636 return VLC_EGENERIC;
1638 *pi_pen_x = i_pen_x_start;
1640 p_line->i_width = line.xMax;
1641 p_line->i_height = __MAX( p_line->i_height,
1642 p_face->size->metrics.height >> 6 );
1643 p_line->pp_glyphs[ i ] = NULL;
1645 p_result->x = __MAX( p_result->x, line.xMax );
1646 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1647 i_yMax - i_yMin ) );
1652 *psz_unicode = '\n';
1654 psz_unicode = psz_unicode_start;
1655 *pi_pen_x = i_pen_x_start;
1663 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1664 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1666 i_previous = i_glyph_index;
1667 *pi_pen_x += p_face->glyph->advance.x >> 6;
1670 p_line->i_width = line.xMax;
1671 p_line->i_height = __MAX( p_line->i_height,
1672 p_face->size->metrics.height >> 6 );
1673 p_line->pp_glyphs[ i ] = NULL;
1675 p_result->x = __MAX( p_result->x, line.xMax );
1676 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1677 line.yMax - line.yMin ) );
1681 /* Get rid of any text processed - if necessary repositioning
1682 * at the start of a new line of text
1686 *psz_unicode_start = '\0';
1688 else if( psz_unicode > psz_unicode_start )
1690 for( i=0; psz_unicode[ i ]; i++ )
1691 psz_unicode_start[ i ] = psz_unicode[ i ];
1692 psz_unicode_start[ i ] = '\0';
1698 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1699 uint32_t **psz_text_out, uint32_t *pi_runs,
1700 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1701 ft_style_t *p_style )
1703 uint32_t i_string_length = 0;
1705 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1706 *psz_text_out += i_string_length;
1708 if( ppp_styles && ppi_run_lengths )
1714 *ppp_styles = (ft_style_t **)
1715 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1717 else if( *pi_runs == 1 )
1719 *ppp_styles = (ft_style_t **)
1720 malloc( *pi_runs * sizeof( ft_style_t * ) );
1723 /* We have just malloc'ed this memory successfully -
1724 * *pi_runs HAS to be within the memory area of *ppp_styles */
1727 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1731 if( *ppi_run_lengths )
1733 *ppi_run_lengths = (uint32_t *)
1734 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1736 else if( *pi_runs == 1 )
1738 *ppi_run_lengths = (uint32_t *)
1739 malloc( *pi_runs * sizeof( uint32_t ) );
1742 /* same remarks here */
1743 if( *ppi_run_lengths )
1745 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1748 /* If we couldn't use the p_style argument due to memory allocation
1749 * problems above, release it here.
1751 if( p_style ) DeleteStyle( p_style );
1754 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1758 for( k=0; k < p_sys->i_font_attachments; k++ )
1760 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1762 FT_Face p_face = NULL;
1764 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1772 bool match = !strcasecmp( p_face->family_name,
1773 p_style->psz_fontname );
1775 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1776 match = match && p_style->b_bold;
1778 match = match && !p_style->b_bold;
1780 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1781 match = match && p_style->b_italic;
1783 match = match && !p_style->b_italic;
1791 FT_Done_Face( p_face );
1796 return VLC_EGENERIC;
1799 static int ProcessLines( filter_t *p_filter,
1804 uint32_t *pi_run_lengths,
1805 ft_style_t **pp_styles,
1806 line_desc_t **pp_lines,
1808 FT_Vector *p_result,
1812 uint32_t *pi_k_run_lengths,
1813 uint32_t *pi_k_durations )
1815 filter_sys_t *p_sys = p_filter->p_sys;
1816 ft_style_t **pp_char_styles;
1817 int *p_new_positions = NULL;
1818 int8_t *p_levels = NULL;
1819 uint8_t *pi_karaoke_bar = NULL;
1823 /* Assign each character in the text string its style explicitly, so that
1824 * after the characters have been shuffled around by Fribidi, we can re-apply
1825 * the styles, and to simplify the calculation of runs within a line.
1827 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1828 if( !pp_char_styles )
1833 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1834 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1835 * we just won't be able to display the progress bar; at least we'll
1841 for( j = 0; j < i_runs; j++ )
1842 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1843 pp_char_styles[ i++ ] = pp_styles[ j ];
1845 #if defined(HAVE_FRIBIDI)
1847 ft_style_t **pp_char_styles_new;
1848 int *p_old_positions;
1849 uint32_t *p_fribidi_string;
1850 int start_pos, pos = 0;
1852 pp_char_styles_new = (ft_style_t **)
1853 malloc( i_len * sizeof( ft_style_t * ));
1855 p_fribidi_string = (uint32_t *)
1856 malloc( (i_len + 1) * sizeof(uint32_t) );
1857 p_old_positions = (int *)
1858 malloc( (i_len + 1) * sizeof( int ) );
1859 p_new_positions = (int *)
1860 malloc( (i_len + 1) * sizeof( int ) );
1861 p_levels = (int8_t *)
1862 malloc( (i_len + 1) * sizeof( int8_t ) );
1864 if( ! pp_char_styles_new ||
1865 ! p_fribidi_string ||
1866 ! p_old_positions ||
1867 ! p_new_positions ||
1871 free( p_old_positions );
1872 free( p_new_positions );
1873 free( p_fribidi_string );
1874 free( pp_char_styles_new );
1875 free( pi_karaoke_bar );
1877 free( pp_char_styles );
1881 /* Do bidi conversion line-by-line */
1884 while(pos < i_len) {
1885 if (psz_text[pos] != '\n')
1887 p_fribidi_string[pos] = psz_text[pos];
1888 pp_char_styles_new[pos] = pp_char_styles[pos];
1889 p_new_positions[pos] = pos;
1894 while(pos < i_len) {
1895 if (psz_text[pos] == '\n')
1899 if (pos > start_pos)
1901 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1902 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1903 pos - start_pos, &base_dir,
1904 (FriBidiChar*)p_fribidi_string + start_pos,
1905 p_new_positions + start_pos,
1907 p_levels + start_pos );
1908 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1910 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1911 p_old_positions[ j - start_pos ] ];
1912 p_new_positions[ j ] += start_pos;
1916 free( p_old_positions );
1917 free( pp_char_styles );
1918 pp_char_styles = pp_char_styles_new;
1919 psz_text = p_fribidi_string;
1920 p_fribidi_string[ i_len ] = 0;
1923 /* Work out the karaoke */
1924 if( pi_karaoke_bar )
1926 int64_t i_last_duration = 0;
1927 int64_t i_duration = 0;
1928 int64_t i_start_pos = 0;
1929 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1931 for( k = 0; k< i_k_runs; k++ )
1933 double fraction = 0.0;
1935 i_duration += pi_k_durations[ k ];
1937 if( i_duration < i_elapsed )
1939 /* Completely finished this run-length -
1940 * let it render normally */
1944 else if( i_elapsed < i_last_duration )
1946 /* Haven't got up to this segment yet -
1947 * render it completely in karaoke BG mode */
1953 /* Partway through this run */
1955 fraction = (double)(i_elapsed - i_last_duration) /
1956 (double)pi_k_durations[ k ];
1958 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1960 double shade = pi_k_run_lengths[ k ] * fraction;
1962 if( p_new_positions )
1963 j = p_new_positions[ i_start_pos + i ];
1965 j = i_start_pos + i;
1967 if( i < (uint32_t)shade )
1968 pi_karaoke_bar[ j ] = 0xff;
1969 else if( (double)i > shade )
1970 pi_karaoke_bar[ j ] = 0x00;
1973 shade -= (int)shade;
1974 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1975 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1979 i_last_duration = i_duration;
1980 i_start_pos += pi_k_run_lengths[ k ];
1984 free( p_new_positions );
1986 FT_Vector tmp_result;
1988 line_desc_t *p_line = NULL;
1989 line_desc_t *p_prev = NULL;
1995 p_result->x = p_result->y = 0;
1996 tmp_result.x = tmp_result.y = 0;
1999 for( k = 0; k <= (uint32_t) i_len; k++ )
2001 if( ( k == (uint32_t) i_len ) ||
2003 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2005 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2007 /* End of the current style run */
2008 FT_Face p_face = NULL;
2011 /* Look for a match amongst our attachments first */
2012 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2016 char *psz_fontfile = NULL;
2018 vlc_mutex_lock( &fb_lock );
2019 if( p_sys->b_fontconfig_ok )
2021 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2022 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2023 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2024 p_style->psz_fontname,
2029 vlc_mutex_unlock( &fb_lock );
2031 if( psz_fontfile && ! *psz_fontfile )
2033 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2034 " so using default font", p_style->psz_fontname,
2035 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2036 (p_style->b_bold ? "(Bold)" :
2037 (p_style->b_italic ? "(Italic)" : ""))) );
2038 free( psz_fontfile );
2039 psz_fontfile = NULL;
2044 if( FT_New_Face( p_sys->p_library,
2045 psz_fontfile, i_idx, &p_face ) )
2047 free( psz_fontfile );
2048 free( pp_char_styles );
2049 #if defined(HAVE_FRIBIDI)
2052 free( pi_karaoke_bar );
2053 return VLC_EGENERIC;
2055 free( psz_fontfile );
2059 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2061 /* We've loaded a font face which is unhelpful for actually
2062 * rendering text - fallback to the default one.
2064 FT_Done_Face( p_face );
2068 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2069 ft_encoding_unicode ) ||
2070 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2071 p_style->i_font_size ) )
2073 if( p_face ) FT_Done_Face( p_face );
2074 free( pp_char_styles );
2075 #if defined(HAVE_FRIBIDI)
2078 free( pi_karaoke_bar );
2079 return VLC_EGENERIC;
2081 p_sys->i_use_kerning =
2082 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2085 uint32_t *psz_unicode = (uint32_t *)
2086 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2089 if( p_face ) FT_Done_Face( p_face );
2090 free( pp_char_styles );
2091 free( psz_unicode );
2092 #if defined(HAVE_FRIBIDI)
2095 free( pi_karaoke_bar );
2098 memcpy( psz_unicode, psz_text + i_prev,
2099 sizeof( uint32_t ) * ( k - i_prev ) );
2100 psz_unicode[ k - i_prev ] = 0;
2101 while( *psz_unicode )
2105 if( !(p_line = NewLine( i_len - i_prev)) )
2107 if( p_face ) FT_Done_Face( p_face );
2108 free( pp_char_styles );
2109 free( psz_unicode );
2110 #if defined(HAVE_FRIBIDI)
2113 free( pi_karaoke_bar );
2116 /* New Color mode only works in YUVA rendering mode --
2117 * (RGB mode has palette constraints on it). We therefore
2118 * need to populate the legacy colour fields also.
2120 p_line->b_new_color_mode = true;
2121 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2122 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2123 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2124 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2125 p_line->p_next = NULL;
2127 i_pen_y += tmp_result.y;
2131 if( p_prev ) p_prev->p_next = p_line;
2132 else *pp_lines = p_line;
2135 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2136 p_style->i_font_color, p_style->b_underline,
2137 p_style->i_karaoke_bg_color,
2138 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2139 &tmp_result ) != VLC_SUCCESS )
2141 if( p_face ) FT_Done_Face( p_face );
2142 free( pp_char_styles );
2143 free( psz_unicode );
2144 #if defined(HAVE_FRIBIDI)
2147 free( pi_karaoke_bar );
2148 return VLC_EGENERIC;
2153 p_result->x = __MAX( p_result->x, tmp_result.x );
2154 p_result->y += tmp_result.y;
2159 if( *psz_unicode == '\n')
2163 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2165 *c_ptr = *(c_ptr+1);
2170 free( psz_unicode );
2171 if( p_face ) FT_Done_Face( p_face );
2175 free( pp_char_styles );
2176 #if defined(HAVE_FRIBIDI)
2181 p_result->x = __MAX( p_result->x, tmp_result.x );
2182 p_result->y += tmp_result.y;
2185 if( pi_karaoke_bar )
2188 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2190 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2192 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2196 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2198 /* 100% BG colour will render faster if we
2199 * instead make it 100% FG colour, so leave
2200 * the ratio alone and copy the value across
2202 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2206 if( pi_karaoke_bar[ i ] & 0x80 )
2208 /* Swap Left and Right sides over for Right aligned
2209 * language text (eg. Arabic, Hebrew)
2211 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2213 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2214 p_line->p_bg_rgb[ k ] = i_tmp;
2216 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2219 /* Jump over the '\n' at the line-end */
2222 free( pi_karaoke_bar );
2228 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2229 subpicture_region_t *p_region_in )
2231 int rv = VLC_SUCCESS;
2232 stream_t *p_sub = NULL;
2233 xml_t *p_xml = NULL;
2234 xml_reader_t *p_xml_reader = NULL;
2236 if( !p_region_in || !p_region_in->psz_html )
2237 return VLC_EGENERIC;
2239 /* Reset the default fontsize in case screen metrics have changed */
2240 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2242 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2243 (uint8_t *) p_region_in->psz_html,
2244 strlen( p_region_in->psz_html ),
2248 p_xml = xml_Create( p_filter );
2251 bool b_karaoke = false;
2253 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2256 /* Look for Root Node */
2257 if( xml_ReaderRead( p_xml_reader ) == 1 )
2259 char *psz_node = xml_ReaderName( p_xml_reader );
2261 if( !strcasecmp( "karaoke", psz_node ) )
2263 /* We're going to have to render the text a number
2264 * of times to show the progress marker on the text.
2266 var_SetBool( p_filter, "text-rerender", true );
2269 else if( !strcasecmp( "text", psz_node ) )
2275 /* Only text and karaoke tags are supported */
2276 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2277 xml_ReaderDelete( p_xml, p_xml_reader );
2278 p_xml_reader = NULL;
2290 uint32_t i_runs = 0;
2291 uint32_t i_k_runs = 0;
2292 uint32_t *pi_run_lengths = NULL;
2293 uint32_t *pi_k_run_lengths = NULL;
2294 uint32_t *pi_k_durations = NULL;
2295 ft_style_t **pp_styles = NULL;
2297 line_desc_t *p_lines = NULL;
2299 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2300 sizeof( uint32_t ) );
2305 rv = ProcessNodes( p_filter, p_xml_reader,
2306 p_region_in->p_style, psz_text, &i_len,
2307 &i_runs, &pi_run_lengths, &pp_styles,
2309 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2312 p_region_out->i_x = p_region_in->i_x;
2313 p_region_out->i_y = p_region_in->i_y;
2315 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2317 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2318 pi_run_lengths, pp_styles, &p_lines, &result,
2319 b_karaoke, i_k_runs, pi_k_run_lengths,
2323 for( k=0; k<i_runs; k++)
2324 DeleteStyle( pp_styles[k] );
2326 free( pi_run_lengths );
2329 /* Don't attempt to render text that couldn't be layed out
2332 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2334 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2336 Render( p_filter, p_region_out, p_lines,
2337 result.x, result.y );
2341 RenderYUVA( p_filter, p_region_out, p_lines,
2342 result.x, result.y );
2346 FreeLines( p_lines );
2348 xml_ReaderDelete( p_xml, p_xml_reader );
2350 xml_Delete( p_xml );
2352 stream_Delete( p_sub );
2358 static char* FontConfig_Select( FcConfig* priv, const char* family,
2359 bool b_bold, bool b_italic, int *i_idx )
2362 FcPattern *pat, *p_pat;
2366 pat = FcPatternCreate();
2367 if (!pat) return NULL;
2369 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2370 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2371 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2372 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2374 FcDefaultSubstitute( pat );
2376 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2378 FcPatternDestroy( pat );
2382 p_pat = FcFontMatch( priv, pat, &result );
2383 FcPatternDestroy( pat );
2384 if( !p_pat ) return NULL;
2386 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2387 || ( val_b != FcTrue ) )
2389 FcPatternDestroy( p_pat );
2392 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2397 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2399 FcPatternDestroy( p_pat );
2404 if( strcasecmp((const char*)val_s, family ) != 0 )
2405 msg_Warn( p_filter, "fontconfig: selected font family is not"
2406 "the requested one: '%s' != '%s'\n",
2407 (const char*)val_s, family );
2410 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2412 FcPatternDestroy( p_pat );
2416 FcPatternDestroy( p_pat );
2417 return strdup( (const char*)val_s );
2421 static void FreeLine( line_desc_t *p_line )
2424 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2426 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2428 free( p_line->pp_glyphs );
2429 free( p_line->p_glyph_pos );
2430 free( p_line->p_fg_rgb );
2431 free( p_line->p_bg_rgb );
2432 free( p_line->p_fg_bg_ratio );
2433 free( p_line->pi_underline_offset );
2434 free( p_line->pi_underline_thickness );
2438 static void FreeLines( line_desc_t *p_lines )
2440 line_desc_t *p_line, *p_next;
2442 if( !p_lines ) return;
2444 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2446 p_next = p_line->p_next;
2451 static line_desc_t *NewLine( int i_count )
2453 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2455 if( !p_line ) return NULL;
2456 p_line->i_height = 0;
2457 p_line->i_width = 0;
2458 p_line->p_next = NULL;
2460 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2461 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2462 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2463 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2464 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2465 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2466 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2467 if( ( p_line->pp_glyphs == NULL ) ||
2468 ( p_line->p_glyph_pos == NULL ) ||
2469 ( p_line->p_fg_rgb == NULL ) ||
2470 ( p_line->p_bg_rgb == NULL ) ||
2471 ( p_line->p_fg_bg_ratio == NULL ) ||
2472 ( p_line->pi_underline_offset == NULL ) ||
2473 ( p_line->pi_underline_thickness == NULL ) )
2475 free( p_line->pi_underline_thickness );
2476 free( p_line->pi_underline_offset );
2477 free( p_line->p_fg_rgb );
2478 free( p_line->p_bg_rgb );
2479 free( p_line->p_fg_bg_ratio );
2480 free( p_line->p_glyph_pos );
2481 free( p_line->pp_glyphs );
2485 p_line->pp_glyphs[0] = NULL;
2486 p_line->b_new_color_mode = false;
2491 static int GetFontSize( filter_t *p_filter )
2493 filter_sys_t *p_sys = p_filter->p_sys;
2497 if( p_sys->i_default_font_size )
2499 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2500 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2502 i_size = p_sys->i_default_font_size;
2506 var_Get( p_filter, "freetype-rel-fontsize", &val );
2509 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2510 p_filter->p_sys->i_display_height =
2511 p_filter->fmt_out.video.i_height;
2516 msg_Warn( p_filter, "invalid fontsize, using 12" );
2517 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2518 i_size = 12 * val.i_int / 1000;
2525 static int SetFontSize( filter_t *p_filter, int i_size )
2527 filter_sys_t *p_sys = p_filter->p_sys;
2531 i_size = GetFontSize( p_filter );
2533 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2536 p_sys->i_font_size = i_size;
2538 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2540 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2541 return VLC_EGENERIC;
2547 static void YUVFromRGB( uint32_t i_argb,
2548 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2550 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2551 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2552 int i_blue = ( i_argb & 0x000000ff );
2554 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2555 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2556 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2557 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2558 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2559 -585 * i_blue + 4096 + 1048576) >> 13, 240);