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();
517 vlc_thread_ready( p_this );
522 int canc = vlc_savecancel ();
524 //msg_Dbg( p_this, "Building font database..." );
525 msg_Dbg( p_this, "Building font database..." );
527 if(! FcConfigBuildFonts( p_fontconfig ))
529 /* Don't destroy the fontconfig object - we won't be able to do
530 * italics or bold or change the font face, but we will still
531 * be able to do underline and change the font size.
533 msg_Err( p_this, "fontconfig database can't be built. "
534 "Font styling won't be available" );
538 msg_Dbg( p_this, "Finished building font database." );
539 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
541 vlc_mutex_lock( &fb_lock );
542 p_this->p_private = p_fontconfig;
543 vlc_mutex_unlock( &fb_lock );
545 var_SetBool( p_this, "build-done", true );
546 vlc_restorecancel (canc);
550 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
552 filter_sys_t *p_sys = p_filter->p_sys;
554 p_sys->p_fontconfig = p_fontbuilder->p_private;
555 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
557 static void FontBuilderDestructor( vlc_object_t *p_this )
559 FcConfig *p_fontconfig = p_this->p_private;
562 FcConfigDestroy( p_fontconfig );
564 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
565 vlc_value_t oldval, vlc_value_t newval, void *param )
567 filter_t *p_filter = param;
571 vlc_mutex_lock( &fb_lock );
573 FontBuilderGetFcConfig( p_filter, p_this );
575 vlc_mutex_unlock( &fb_lock );
584 /*****************************************************************************
585 * Make any TTF/OTF fonts present in the attachments of the media file
586 * and store them for later use by the FreeType Engine
587 *****************************************************************************/
588 static int LoadFontsFromAttachments( filter_t *p_filter )
590 filter_sys_t *p_sys = p_filter->p_sys;
591 input_thread_t *p_input;
592 input_attachment_t **pp_attachments;
593 int i_attachments_cnt;
595 int rv = VLC_SUCCESS;
597 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
601 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
603 vlc_object_release(p_input);
607 p_sys->i_font_attachments = 0;
608 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
609 if(! p_sys->pp_font_attachments )
612 for( k = 0; k < i_attachments_cnt; k++ )
614 input_attachment_t *p_attach = pp_attachments[k];
616 if( p_sys->pp_font_attachments )
618 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
619 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
620 ( p_attach->i_data > 0 ) &&
621 ( p_attach->p_data != NULL ) )
623 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
627 vlc_input_attachment_Delete( p_attach );
632 vlc_input_attachment_Delete( p_attach );
635 free( pp_attachments );
637 vlc_object_release(p_input);
642 /*****************************************************************************
643 * Render: place string in picture
644 *****************************************************************************
645 * This function merges the previously rendered freetype glyphs into a picture
646 *****************************************************************************/
647 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
648 line_desc_t *p_line, int i_width, int i_height )
650 static const uint8_t pi_gamma[16] =
651 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
652 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
656 int i, x, y, i_pitch;
657 uint8_t i_y; /* YUV values, derived from incoming RGB */
660 /* Create a new subpicture region */
661 memset( &fmt, 0, sizeof(video_format_t) );
662 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
664 fmt.i_width = fmt.i_visible_width = i_width + 4;
665 fmt.i_height = fmt.i_visible_height = i_height + 4;
666 if( p_region->fmt.i_visible_width > 0 )
667 fmt.i_visible_width = p_region->fmt.i_visible_width;
668 if( p_region->fmt.i_visible_height > 0 )
669 fmt.i_visible_height = p_region->fmt.i_visible_height;
670 fmt.i_x_offset = fmt.i_y_offset = 0;
672 assert( !p_region->p_picture );
673 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
674 if( !p_region->p_picture )
678 /* Calculate text color components */
679 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
680 25 * p_line->i_blue + 128) >> 8) + 16;
681 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
682 112 * p_line->i_blue + 128) >> 8) + 128;
683 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
684 18 * p_line->i_blue + 128) >> 8) + 128;
687 fmt.p_palette->i_entries = 16;
688 for( i = 0; i < 8; i++ )
690 fmt.p_palette->palette[i][0] = 0;
691 fmt.p_palette->palette[i][1] = 0x80;
692 fmt.p_palette->palette[i][2] = 0x80;
693 fmt.p_palette->palette[i][3] = pi_gamma[i];
694 fmt.p_palette->palette[i][3] =
695 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
697 for( i = 8; i < fmt.p_palette->i_entries; i++ )
699 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
700 fmt.p_palette->palette[i][1] = i_u;
701 fmt.p_palette->palette[i][2] = i_v;
702 fmt.p_palette->palette[i][3] = pi_gamma[i];
703 fmt.p_palette->palette[i][3] =
704 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
707 p_dst = p_region->p_picture->Y_PIXELS;
708 i_pitch = p_region->p_picture->Y_PITCH;
710 /* Initialize the region pixels */
711 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
713 for( ; p_line != NULL; p_line = p_line->p_next )
715 int i_glyph_tmax = 0;
716 int i_bitmap_offset, i_offset, i_align_offset = 0;
717 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
719 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
720 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
723 if( p_line->i_width < i_width )
725 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
727 i_align_offset = i_width - p_line->i_width;
729 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
731 i_align_offset = ( i_width - p_line->i_width ) / 2;
735 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
737 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
739 i_offset = ( p_line->p_glyph_pos[ i ].y +
740 i_glyph_tmax - p_glyph->top + 2 ) *
741 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
744 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
746 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
748 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
750 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
757 /* Outlining (find something better than nearest neighbour filtering ?) */
760 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
761 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
762 uint8_t left, current;
764 for( y = 1; y < (int)fmt.i_height - 1; y++ )
766 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
767 p_dst += p_region->p_picture->Y_PITCH;
770 for( x = 1; x < (int)fmt.i_width - 1; x++ )
773 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
774 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;
778 memset( p_top, 0, fmt.i_width );
784 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
785 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
786 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
787 int i_glyph_tmax, int i_align_offset,
788 uint8_t i_y, uint8_t i_u, uint8_t i_v,
789 subpicture_region_t *p_region)
793 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
795 p_dst_y = p_region->p_picture->Y_PIXELS;
796 p_dst_u = p_region->p_picture->U_PIXELS;
797 p_dst_v = p_region->p_picture->V_PIXELS;
798 p_dst_a = p_region->p_picture->A_PIXELS;
799 i_pitch = p_region->p_picture->A_PITCH;
801 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
802 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
804 for( y = 0; y < i_line_thickness; y++ )
806 int i_extra = p_this_glyph->bitmap.width;
810 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
811 (p_this_glyph_pos->x + p_this_glyph->left);
813 for( x = 0; x < i_extra; x++ )
817 /* break the underline around the tails of any glyphs which cross it */
818 for( z = x - i_line_thickness;
819 z < x + i_line_thickness && b_ok;
822 if( p_next_glyph && ( z >= i_extra ) )
824 int i_row = i_line_offset + p_next_glyph->top + y;
826 if( ( p_next_glyph->bitmap.rows > i_row ) &&
827 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
832 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
834 int i_row = i_line_offset + p_this_glyph->top + y;
836 if( ( p_this_glyph->bitmap.rows > i_row ) &&
837 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
846 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
847 p_dst_u[i_offset+x] = i_u;
848 p_dst_v[i_offset+x] = i_v;
849 p_dst_a[i_offset+x] = 255;
856 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
858 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
859 int i_pitch = p_region->p_picture->A_PITCH;
862 for( ; p_line != NULL; p_line = p_line->p_next )
864 int i_glyph_tmax=0, i = 0;
865 int i_bitmap_offset, i_offset, i_align_offset = 0;
866 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
868 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
869 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
872 if( p_line->i_width < i_width )
874 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
876 i_align_offset = i_width - p_line->i_width;
878 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
880 i_align_offset = ( i_width - p_line->i_width ) / 2;
884 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
886 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
888 i_offset = ( p_line->p_glyph_pos[ i ].y +
889 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
890 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
891 i_align_offset +xoffset;
893 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
895 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
897 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
898 if( p_dst[i_offset+x] <
899 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
901 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
910 /*****************************************************************************
911 * Render: place string in picture
912 *****************************************************************************
913 * This function merges the previously rendered freetype glyphs into a picture
914 *****************************************************************************/
915 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
916 line_desc_t *p_line, int i_width, int i_height )
918 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
920 int i, x, y, i_pitch, i_alpha;
921 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
923 if( i_width == 0 || i_height == 0 )
926 /* Create a new subpicture region */
927 memset( &fmt, 0, sizeof(video_format_t) );
928 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
930 fmt.i_width = fmt.i_visible_width = i_width + 6;
931 fmt.i_height = fmt.i_visible_height = i_height + 6;
932 if( p_region->fmt.i_visible_width > 0 )
933 fmt.i_visible_width = p_region->fmt.i_visible_width;
934 if( p_region->fmt.i_visible_height > 0 )
935 fmt.i_visible_height = p_region->fmt.i_visible_height;
936 fmt.i_x_offset = fmt.i_y_offset = 0;
938 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
939 if( !p_region->p_picture )
943 /* Calculate text color components */
944 YUVFromRGB( (p_line->i_red << 16) |
945 (p_line->i_green << 8) |
948 i_alpha = p_line->i_alpha;
950 p_dst_y = p_region->p_picture->Y_PIXELS;
951 p_dst_u = p_region->p_picture->U_PIXELS;
952 p_dst_v = p_region->p_picture->V_PIXELS;
953 p_dst_a = p_region->p_picture->A_PIXELS;
954 i_pitch = p_region->p_picture->A_PITCH;
956 /* Initialize the region pixels */
957 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
959 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
960 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
961 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
962 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
966 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
967 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
968 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
969 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
971 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
972 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
974 DrawBlack( p_line, i_width, p_region, 0, 0);
975 DrawBlack( p_line, i_width, p_region, -1, 0);
976 DrawBlack( p_line, i_width, p_region, 0, -1);
977 DrawBlack( p_line, i_width, p_region, 1, 0);
978 DrawBlack( p_line, i_width, p_region, 0, 1);
981 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
983 DrawBlack( p_line, i_width, p_region, -1, -1);
984 DrawBlack( p_line, i_width, p_region, -1, 1);
985 DrawBlack( p_line, i_width, p_region, 1, -1);
986 DrawBlack( p_line, i_width, p_region, 1, 1);
988 DrawBlack( p_line, i_width, p_region, -2, 0);
989 DrawBlack( p_line, i_width, p_region, 0, -2);
990 DrawBlack( p_line, i_width, p_region, 2, 0);
991 DrawBlack( p_line, i_width, p_region, 0, 2);
993 DrawBlack( p_line, i_width, p_region, -2, -2);
994 DrawBlack( p_line, i_width, p_region, -2, 2);
995 DrawBlack( p_line, i_width, p_region, 2, -2);
996 DrawBlack( p_line, i_width, p_region, 2, 2);
998 DrawBlack( p_line, i_width, p_region, -3, 0);
999 DrawBlack( p_line, i_width, p_region, 0, -3);
1000 DrawBlack( p_line, i_width, p_region, 3, 0);
1001 DrawBlack( p_line, i_width, p_region, 0, 3);
1004 for( ; p_line != NULL; p_line = p_line->p_next )
1006 int i_glyph_tmax = 0;
1007 int i_bitmap_offset, i_offset, i_align_offset = 0;
1008 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1010 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1011 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1014 if( p_line->i_width < i_width )
1016 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1018 i_align_offset = i_width - p_line->i_width;
1020 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1022 i_align_offset = ( i_width - p_line->i_width ) / 2;
1026 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1028 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1030 i_offset = ( p_line->p_glyph_pos[ i ].y +
1031 i_glyph_tmax - p_glyph->top + 3 ) *
1032 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1035 if( p_line->b_new_color_mode )
1037 /* Every glyph can (and in fact must) have its own color */
1038 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1041 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1043 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1045 uint8_t i_y_local = i_y;
1046 uint8_t i_u_local = i_u;
1047 uint8_t i_v_local = i_v;
1049 if( p_line->p_fg_bg_ratio != 0x00 )
1051 int i_split = p_glyph->bitmap.width *
1052 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1056 YUVFromRGB( p_line->p_bg_rgb[ i ],
1057 &i_y_local, &i_u_local, &i_v_local );
1061 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1063 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1064 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1066 p_dst_u[i_offset+x] = i_u;
1067 p_dst_v[i_offset+x] = i_v;
1069 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1070 p_dst_a[i_offset+x] = 0xff;
1073 i_offset += i_pitch;
1076 if( p_line->pi_underline_thickness[ i ] )
1078 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1079 p_line->pi_underline_offset[ i ],
1080 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1081 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1082 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1083 i_glyph_tmax, i_align_offset,
1090 /* Apply the alpha setting */
1091 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1092 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1098 * This function renders a text subpicture region into another one.
1099 * It also calculates the size needed for this string, and renders the
1100 * needed glyphs into memory. It is used as pf_add_string callback in
1101 * the vout method by this module
1103 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1104 subpicture_region_t *p_region_in )
1106 filter_sys_t *p_sys = p_filter->p_sys;
1107 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1108 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1109 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1110 int i_string_length;
1112 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1113 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1123 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1124 psz_string = p_region_in->psz_text;
1125 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1127 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1128 i_scale = val.i_int;
1130 if( p_region_in->p_style )
1132 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1133 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1134 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1138 i_font_color = p_sys->i_font_color;
1139 i_font_alpha = 255 - p_sys->i_font_opacity;
1140 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1143 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1144 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1145 SetFontSize( p_filter, i_font_size );
1147 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1148 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1149 i_blue = i_font_color & 0x000000FF;
1151 result.x = result.y = 0;
1152 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1154 psz_unicode = psz_unicode_orig =
1155 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1156 if( psz_unicode == NULL )
1158 #if defined(WORDS_BIGENDIAN)
1159 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1161 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1163 if( iconv_handle == (vlc_iconv_t)-1 )
1165 msg_Warn( p_filter, "unable to do conversion" );
1171 const char *p_in_buffer = psz_string;
1172 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1173 i_in_bytes = strlen( psz_string );
1174 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1175 i_out_bytes_left = i_out_bytes;
1176 p_out_buffer = (char *)psz_unicode;
1177 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1179 &p_out_buffer, &i_out_bytes_left );
1181 vlc_iconv_close( iconv_handle );
1185 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1186 "bytes left %u", (unsigned)i_in_bytes );
1189 *(uint32_t*)p_out_buffer = 0;
1190 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1193 #if defined(HAVE_FRIBIDI)
1195 uint32_t *p_fribidi_string;
1196 int32_t start_pos, pos = 0;
1198 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1199 if( !p_fribidi_string )
1202 /* Do bidi conversion line-by-line */
1203 while( pos < i_string_length )
1205 while( pos < i_string_length )
1207 i_char = psz_unicode[pos];
1208 if (i_char != '\r' && i_char != '\n')
1210 p_fribidi_string[pos] = i_char;
1214 while( pos < i_string_length )
1216 i_char = psz_unicode[pos];
1217 if (i_char == '\r' || i_char == '\n')
1221 if (pos > start_pos)
1223 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1224 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1227 (FriBidiChar*)p_fribidi_string + start_pos,
1232 free( psz_unicode_orig );
1233 psz_unicode = psz_unicode_orig = p_fribidi_string;
1234 p_fribidi_string[ i_string_length ] = 0;
1238 /* Calculate relative glyph positions and a bounding box for the
1240 if( !(p_line = NewLine( strlen( psz_string ))) )
1243 i_pen_x = i_pen_y = 0;
1245 psz_line_start = psz_unicode;
1247 #define face p_sys->p_face
1248 #define glyph face->glyph
1250 while( *psz_unicode )
1252 i_char = *psz_unicode++;
1253 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1258 if( i_char == '\n' )
1260 psz_line_start = psz_unicode;
1261 if( !(p_next = NewLine( strlen( psz_string ))) )
1263 p_line->p_next = p_next;
1264 p_line->i_width = line.xMax;
1265 p_line->i_height = face->size->metrics.height >> 6;
1266 p_line->pp_glyphs[ i ] = NULL;
1267 p_line->i_alpha = i_font_alpha;
1268 p_line->i_red = i_red;
1269 p_line->i_green = i_green;
1270 p_line->i_blue = i_blue;
1273 result.x = __MAX( result.x, line.xMax );
1274 result.y += face->size->metrics.height >> 6;
1277 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1278 i_pen_y += face->size->metrics.height >> 6;
1280 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1285 i_glyph_index = FT_Get_Char_Index( face, i_char );
1286 if( p_sys->i_use_kerning && i_glyph_index
1290 FT_Get_Kerning( face, i_previous, i_glyph_index,
1291 ft_kerning_default, &delta );
1292 i_pen_x += delta.x >> 6;
1295 p_line->p_glyph_pos[ i ].x = i_pen_x;
1296 p_line->p_glyph_pos[ i ].y = i_pen_y;
1297 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1300 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1304 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1307 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1311 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1312 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1315 FT_Done_Glyph( tmp_glyph );
1318 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1321 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1322 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1323 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1325 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1326 p_line->pp_glyphs[ i ] = NULL;
1328 p_line = NewLine( strlen( psz_string ));
1329 if( p_prev ) p_prev->p_next = p_line;
1330 else p_lines = p_line;
1332 uint32_t *psz_unicode_saved = psz_unicode;
1333 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1337 if( psz_unicode == psz_line_start )
1338 { /* try harder to break that line */
1339 psz_unicode = psz_unicode_saved;
1340 while( psz_unicode > psz_line_start &&
1341 *psz_unicode != '_' && *psz_unicode != '/' &&
1342 *psz_unicode != '\\' && *psz_unicode != '.' )
1347 if( psz_unicode == psz_line_start )
1349 msg_Warn( p_filter, "unbreakable string" );
1354 *psz_unicode = '\n';
1356 psz_unicode = psz_line_start;
1359 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1362 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1363 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1365 i_previous = i_glyph_index;
1366 i_pen_x += glyph->advance.x >> 6;
1370 p_line->i_width = line.xMax;
1371 p_line->i_height = face->size->metrics.height >> 6;
1372 p_line->pp_glyphs[ i ] = NULL;
1373 p_line->i_alpha = i_font_alpha;
1374 p_line->i_red = i_red;
1375 p_line->i_green = i_green;
1376 p_line->i_blue = i_blue;
1377 result.x = __MAX( result.x, line.xMax );
1378 result.y += line.yMax - line.yMin;
1383 p_region_out->i_x = p_region_in->i_x;
1384 p_region_out->i_y = p_region_in->i_y;
1386 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1387 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1389 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1391 free( psz_unicode_orig );
1392 FreeLines( p_lines );
1396 free( psz_unicode_orig );
1397 FreeLines( p_lines );
1398 return VLC_EGENERIC;
1401 #ifdef HAVE_FONTCONFIG
1402 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1403 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1404 bool b_italic, bool b_uline )
1406 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1410 p_style->i_font_size = i_font_size;
1411 p_style->i_font_color = i_font_color;
1412 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1413 p_style->b_italic = b_italic;
1414 p_style->b_bold = b_bold;
1415 p_style->b_underline = b_uline;
1417 p_style->psz_fontname = strdup( psz_fontname );
1422 static void DeleteStyle( ft_style_t *p_style )
1426 free( p_style->psz_fontname );
1431 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1438 if(( s1->i_font_size == s2->i_font_size ) &&
1439 ( s1->i_font_color == s2->i_font_color ) &&
1440 ( s1->b_italic == s2->b_italic ) &&
1441 ( s1->b_bold == s2->b_bold ) &&
1442 ( s1->b_underline == s2->b_underline ) &&
1443 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1450 static void IconvText( filter_t *p_filter, const char *psz_string,
1451 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1453 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1455 /* If memory hasn't been allocated for our output string, allocate it here
1456 * - the calling function must now be responsible for freeing it.
1458 if( !*ppsz_unicode )
1459 *ppsz_unicode = (uint32_t *)
1460 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1462 /* We don't need to handle a NULL pointer in *ppsz_unicode
1463 * if we are instead testing for a non NULL value like we are here */
1467 #if defined(WORDS_BIGENDIAN)
1468 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1470 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1472 if( iconv_handle != (vlc_iconv_t)-1 )
1474 char *p_in_buffer, *p_out_buffer;
1475 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1476 i_in_bytes = strlen( psz_string );
1477 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1478 i_out_bytes_left = i_out_bytes;
1479 p_in_buffer = (char *) psz_string;
1480 p_out_buffer = (char *) *ppsz_unicode;
1481 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1482 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1484 vlc_iconv_close( iconv_handle );
1488 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1489 "bytes left %u", (unsigned)i_in_bytes );
1493 *(uint32_t*)p_out_buffer = 0;
1495 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1500 msg_Warn( p_filter, "unable to do conversion" );
1505 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1506 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1509 ft_style_t *p_style = NULL;
1511 char *psz_fontname = NULL;
1512 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1513 uint32_t i_karaoke_bg_color = i_font_color;
1514 int i_font_size = p_sys->i_font_size;
1516 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1517 &i_font_color, &i_karaoke_bg_color ))
1519 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1520 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1525 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1526 bool b_uline, int i_karaoke_bgcolor,
1527 line_desc_t *p_line, uint32_t *psz_unicode,
1528 int *pi_pen_x, int i_pen_y, int *pi_start,
1529 FT_Vector *p_result )
1534 bool b_first_on_line = true;
1537 int i_pen_x_start = *pi_pen_x;
1539 uint32_t *psz_unicode_start = psz_unicode;
1541 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1543 /* Account for part of line already in position */
1544 for( i=0; i<*pi_start; i++ )
1548 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1549 ft_glyph_bbox_pixels, &glyph_size );
1551 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1552 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1553 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1554 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1560 b_first_on_line = false;
1562 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1568 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1569 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1573 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1574 ft_kerning_default, &delta );
1575 *pi_pen_x += delta.x >> 6;
1577 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1578 p_line->p_glyph_pos[ i ].y = i_pen_y;
1580 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1584 "unable to render text FT_Load_Glyph returned %d", i_error );
1585 p_line->pp_glyphs[ i ] = NULL;
1586 return VLC_EGENERIC;
1588 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1592 "unable to render text FT_Get_Glyph returned %d", i_error );
1593 p_line->pp_glyphs[ i ] = NULL;
1594 return VLC_EGENERIC;
1596 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1597 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1600 FT_Done_Glyph( tmp_glyph );
1605 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1606 p_face->size->metrics.y_scale));
1607 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1608 p_face->size->metrics.y_scale));
1610 p_line->pi_underline_offset[ i ] =
1611 ( aOffset < 0 ) ? -aOffset : aOffset;
1612 p_line->pi_underline_thickness[ i ] =
1613 ( aSize < 0 ) ? -aSize : aSize;
1615 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1616 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1617 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1618 p_line->p_fg_bg_ratio[ i ] = 0x00;
1620 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1621 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1622 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1624 for( ; i >= *pi_start; i-- )
1625 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1628 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1632 if( psz_unicode == psz_unicode_start )
1634 if( b_first_on_line )
1636 msg_Warn( p_filter, "unbreakable string" );
1637 p_line->pp_glyphs[ i ] = NULL;
1638 return VLC_EGENERIC;
1640 *pi_pen_x = i_pen_x_start;
1642 p_line->i_width = line.xMax;
1643 p_line->i_height = __MAX( p_line->i_height,
1644 p_face->size->metrics.height >> 6 );
1645 p_line->pp_glyphs[ i ] = NULL;
1647 p_result->x = __MAX( p_result->x, line.xMax );
1648 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1649 i_yMax - i_yMin ) );
1654 *psz_unicode = '\n';
1656 psz_unicode = psz_unicode_start;
1657 *pi_pen_x = i_pen_x_start;
1665 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1666 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1668 i_previous = i_glyph_index;
1669 *pi_pen_x += p_face->glyph->advance.x >> 6;
1672 p_line->i_width = line.xMax;
1673 p_line->i_height = __MAX( p_line->i_height,
1674 p_face->size->metrics.height >> 6 );
1675 p_line->pp_glyphs[ i ] = NULL;
1677 p_result->x = __MAX( p_result->x, line.xMax );
1678 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1679 line.yMax - line.yMin ) );
1683 /* Get rid of any text processed - if necessary repositioning
1684 * at the start of a new line of text
1688 *psz_unicode_start = '\0';
1690 else if( psz_unicode > psz_unicode_start )
1692 for( i=0; psz_unicode[ i ]; i++ )
1693 psz_unicode_start[ i ] = psz_unicode[ i ];
1694 psz_unicode_start[ i ] = '\0';
1700 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1701 uint32_t **psz_text_out, uint32_t *pi_runs,
1702 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1703 ft_style_t *p_style )
1705 uint32_t i_string_length = 0;
1707 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1708 *psz_text_out += i_string_length;
1710 if( ppp_styles && ppi_run_lengths )
1716 *ppp_styles = (ft_style_t **)
1717 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1719 else if( *pi_runs == 1 )
1721 *ppp_styles = (ft_style_t **)
1722 malloc( *pi_runs * sizeof( ft_style_t * ) );
1725 /* We have just malloc'ed this memory successfully -
1726 * *pi_runs HAS to be within the memory area of *ppp_styles */
1729 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1733 if( *ppi_run_lengths )
1735 *ppi_run_lengths = (uint32_t *)
1736 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1738 else if( *pi_runs == 1 )
1740 *ppi_run_lengths = (uint32_t *)
1741 malloc( *pi_runs * sizeof( uint32_t ) );
1744 /* same remarks here */
1745 if( *ppi_run_lengths )
1747 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1750 /* If we couldn't use the p_style argument due to memory allocation
1751 * problems above, release it here.
1753 if( p_style ) DeleteStyle( p_style );
1756 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1760 for( k=0; k < p_sys->i_font_attachments; k++ )
1762 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1764 FT_Face p_face = NULL;
1766 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1774 bool match = !strcasecmp( p_face->family_name,
1775 p_style->psz_fontname );
1777 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1778 match = match && p_style->b_bold;
1780 match = match && !p_style->b_bold;
1782 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1783 match = match && p_style->b_italic;
1785 match = match && !p_style->b_italic;
1793 FT_Done_Face( p_face );
1798 return VLC_EGENERIC;
1801 static int ProcessLines( filter_t *p_filter,
1806 uint32_t *pi_run_lengths,
1807 ft_style_t **pp_styles,
1808 line_desc_t **pp_lines,
1810 FT_Vector *p_result,
1814 uint32_t *pi_k_run_lengths,
1815 uint32_t *pi_k_durations )
1817 filter_sys_t *p_sys = p_filter->p_sys;
1818 ft_style_t **pp_char_styles;
1819 int *p_new_positions = NULL;
1820 int8_t *p_levels = NULL;
1821 uint8_t *pi_karaoke_bar = NULL;
1825 /* Assign each character in the text string its style explicitly, so that
1826 * after the characters have been shuffled around by Fribidi, we can re-apply
1827 * the styles, and to simplify the calculation of runs within a line.
1829 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1830 if( !pp_char_styles )
1835 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1836 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1837 * we just won't be able to display the progress bar; at least we'll
1843 for( j = 0; j < i_runs; j++ )
1844 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1845 pp_char_styles[ i++ ] = pp_styles[ j ];
1847 #if defined(HAVE_FRIBIDI)
1849 ft_style_t **pp_char_styles_new;
1850 int *p_old_positions;
1851 uint32_t *p_fribidi_string;
1852 int start_pos, pos = 0;
1854 pp_char_styles_new = (ft_style_t **)
1855 malloc( i_len * sizeof( ft_style_t * ));
1857 p_fribidi_string = (uint32_t *)
1858 malloc( (i_len + 1) * sizeof(uint32_t) );
1859 p_old_positions = (int *)
1860 malloc( (i_len + 1) * sizeof( int ) );
1861 p_new_positions = (int *)
1862 malloc( (i_len + 1) * sizeof( int ) );
1863 p_levels = (int8_t *)
1864 malloc( (i_len + 1) * sizeof( int8_t ) );
1866 if( ! pp_char_styles_new ||
1867 ! p_fribidi_string ||
1868 ! p_old_positions ||
1869 ! p_new_positions ||
1873 free( p_old_positions );
1874 free( p_new_positions );
1875 free( p_fribidi_string );
1876 free( pp_char_styles_new );
1877 free( pi_karaoke_bar );
1879 free( pp_char_styles );
1883 /* Do bidi conversion line-by-line */
1886 while(pos < i_len) {
1887 if (psz_text[pos] != '\n')
1889 p_fribidi_string[pos] = psz_text[pos];
1890 pp_char_styles_new[pos] = pp_char_styles[pos];
1891 p_new_positions[pos] = pos;
1896 while(pos < i_len) {
1897 if (psz_text[pos] == '\n')
1901 if (pos > start_pos)
1903 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1904 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1905 pos - start_pos, &base_dir,
1906 (FriBidiChar*)p_fribidi_string + start_pos,
1907 p_new_positions + start_pos,
1909 p_levels + start_pos );
1910 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1912 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1913 p_old_positions[ j - start_pos ] ];
1914 p_new_positions[ j ] += start_pos;
1918 free( p_old_positions );
1919 free( pp_char_styles );
1920 pp_char_styles = pp_char_styles_new;
1921 psz_text = p_fribidi_string;
1922 p_fribidi_string[ i_len ] = 0;
1925 /* Work out the karaoke */
1926 if( pi_karaoke_bar )
1928 int64_t i_last_duration = 0;
1929 int64_t i_duration = 0;
1930 int64_t i_start_pos = 0;
1931 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1933 for( k = 0; k< i_k_runs; k++ )
1935 double fraction = 0.0;
1937 i_duration += pi_k_durations[ k ];
1939 if( i_duration < i_elapsed )
1941 /* Completely finished this run-length -
1942 * let it render normally */
1946 else if( i_elapsed < i_last_duration )
1948 /* Haven't got up to this segment yet -
1949 * render it completely in karaoke BG mode */
1955 /* Partway through this run */
1957 fraction = (double)(i_elapsed - i_last_duration) /
1958 (double)pi_k_durations[ k ];
1960 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1962 double shade = pi_k_run_lengths[ k ] * fraction;
1964 if( p_new_positions )
1965 j = p_new_positions[ i_start_pos + i ];
1967 j = i_start_pos + i;
1969 if( i < (uint32_t)shade )
1970 pi_karaoke_bar[ j ] = 0xff;
1971 else if( (double)i > shade )
1972 pi_karaoke_bar[ j ] = 0x00;
1975 shade -= (int)shade;
1976 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1977 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1981 i_last_duration = i_duration;
1982 i_start_pos += pi_k_run_lengths[ k ];
1986 free( p_new_positions );
1988 FT_Vector tmp_result;
1990 line_desc_t *p_line = NULL;
1991 line_desc_t *p_prev = NULL;
1997 p_result->x = p_result->y = 0;
1998 tmp_result.x = tmp_result.y = 0;
2001 for( k = 0; k <= (uint32_t) i_len; k++ )
2003 if( ( k == (uint32_t) i_len ) ||
2005 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2007 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2009 /* End of the current style run */
2010 FT_Face p_face = NULL;
2013 /* Look for a match amongst our attachments first */
2014 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2018 char *psz_fontfile = NULL;
2020 vlc_mutex_lock( &fb_lock );
2021 if( p_sys->b_fontconfig_ok )
2023 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2024 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2025 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2026 p_style->psz_fontname,
2031 vlc_mutex_unlock( &fb_lock );
2033 if( psz_fontfile && ! *psz_fontfile )
2035 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2036 " so using default font", p_style->psz_fontname,
2037 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2038 (p_style->b_bold ? "(Bold)" :
2039 (p_style->b_italic ? "(Italic)" : ""))) );
2040 free( psz_fontfile );
2041 psz_fontfile = NULL;
2046 if( FT_New_Face( p_sys->p_library,
2047 psz_fontfile, i_idx, &p_face ) )
2049 free( psz_fontfile );
2050 free( pp_char_styles );
2051 #if defined(HAVE_FRIBIDI)
2054 free( pi_karaoke_bar );
2055 return VLC_EGENERIC;
2057 free( psz_fontfile );
2061 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2063 /* We've loaded a font face which is unhelpful for actually
2064 * rendering text - fallback to the default one.
2066 FT_Done_Face( p_face );
2070 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2071 ft_encoding_unicode ) ||
2072 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2073 p_style->i_font_size ) )
2075 if( p_face ) FT_Done_Face( p_face );
2076 free( pp_char_styles );
2077 #if defined(HAVE_FRIBIDI)
2080 free( pi_karaoke_bar );
2081 return VLC_EGENERIC;
2083 p_sys->i_use_kerning =
2084 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2087 uint32_t *psz_unicode = (uint32_t *)
2088 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2091 if( p_face ) FT_Done_Face( p_face );
2092 free( pp_char_styles );
2093 free( psz_unicode );
2094 #if defined(HAVE_FRIBIDI)
2097 free( pi_karaoke_bar );
2100 memcpy( psz_unicode, psz_text + i_prev,
2101 sizeof( uint32_t ) * ( k - i_prev ) );
2102 psz_unicode[ k - i_prev ] = 0;
2103 while( *psz_unicode )
2107 if( !(p_line = NewLine( i_len - i_prev)) )
2109 if( p_face ) FT_Done_Face( p_face );
2110 free( pp_char_styles );
2111 free( psz_unicode );
2112 #if defined(HAVE_FRIBIDI)
2115 free( pi_karaoke_bar );
2118 /* New Color mode only works in YUVA rendering mode --
2119 * (RGB mode has palette constraints on it). We therefore
2120 * need to populate the legacy colour fields also.
2122 p_line->b_new_color_mode = true;
2123 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2124 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2125 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2126 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2127 p_line->p_next = NULL;
2129 i_pen_y += tmp_result.y;
2133 if( p_prev ) p_prev->p_next = p_line;
2134 else *pp_lines = p_line;
2137 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2138 p_style->i_font_color, p_style->b_underline,
2139 p_style->i_karaoke_bg_color,
2140 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2141 &tmp_result ) != VLC_SUCCESS )
2143 if( p_face ) FT_Done_Face( p_face );
2144 free( pp_char_styles );
2145 free( psz_unicode );
2146 #if defined(HAVE_FRIBIDI)
2149 free( pi_karaoke_bar );
2150 return VLC_EGENERIC;
2155 p_result->x = __MAX( p_result->x, tmp_result.x );
2156 p_result->y += tmp_result.y;
2161 if( *psz_unicode == '\n')
2165 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2167 *c_ptr = *(c_ptr+1);
2172 free( psz_unicode );
2173 if( p_face ) FT_Done_Face( p_face );
2177 free( pp_char_styles );
2178 #if defined(HAVE_FRIBIDI)
2183 p_result->x = __MAX( p_result->x, tmp_result.x );
2184 p_result->y += tmp_result.y;
2187 if( pi_karaoke_bar )
2190 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2192 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2194 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2198 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2200 /* 100% BG colour will render faster if we
2201 * instead make it 100% FG colour, so leave
2202 * the ratio alone and copy the value across
2204 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2208 if( pi_karaoke_bar[ i ] & 0x80 )
2210 /* Swap Left and Right sides over for Right aligned
2211 * language text (eg. Arabic, Hebrew)
2213 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2215 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2216 p_line->p_bg_rgb[ k ] = i_tmp;
2218 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2221 /* Jump over the '\n' at the line-end */
2224 free( pi_karaoke_bar );
2230 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2231 subpicture_region_t *p_region_in )
2233 int rv = VLC_SUCCESS;
2234 stream_t *p_sub = NULL;
2235 xml_t *p_xml = NULL;
2236 xml_reader_t *p_xml_reader = NULL;
2238 if( !p_region_in || !p_region_in->psz_html )
2239 return VLC_EGENERIC;
2241 /* Reset the default fontsize in case screen metrics have changed */
2242 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2244 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2245 (uint8_t *) p_region_in->psz_html,
2246 strlen( p_region_in->psz_html ),
2250 p_xml = xml_Create( p_filter );
2253 bool b_karaoke = false;
2255 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2258 /* Look for Root Node */
2259 if( xml_ReaderRead( p_xml_reader ) == 1 )
2261 char *psz_node = xml_ReaderName( p_xml_reader );
2263 if( !strcasecmp( "karaoke", psz_node ) )
2265 /* We're going to have to render the text a number
2266 * of times to show the progress marker on the text.
2268 var_SetBool( p_filter, "text-rerender", true );
2271 else if( !strcasecmp( "text", psz_node ) )
2277 /* Only text and karaoke tags are supported */
2278 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2279 xml_ReaderDelete( p_xml, p_xml_reader );
2280 p_xml_reader = NULL;
2292 uint32_t i_runs = 0;
2293 uint32_t i_k_runs = 0;
2294 uint32_t *pi_run_lengths = NULL;
2295 uint32_t *pi_k_run_lengths = NULL;
2296 uint32_t *pi_k_durations = NULL;
2297 ft_style_t **pp_styles = NULL;
2299 line_desc_t *p_lines = NULL;
2301 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2302 sizeof( uint32_t ) );
2307 rv = ProcessNodes( p_filter, p_xml_reader,
2308 p_region_in->p_style, psz_text, &i_len,
2309 &i_runs, &pi_run_lengths, &pp_styles,
2311 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2314 p_region_out->i_x = p_region_in->i_x;
2315 p_region_out->i_y = p_region_in->i_y;
2317 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2319 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2320 pi_run_lengths, pp_styles, &p_lines, &result,
2321 b_karaoke, i_k_runs, pi_k_run_lengths,
2325 for( k=0; k<i_runs; k++)
2326 DeleteStyle( pp_styles[k] );
2328 free( pi_run_lengths );
2331 /* Don't attempt to render text that couldn't be layed out
2334 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2336 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2338 Render( p_filter, p_region_out, p_lines,
2339 result.x, result.y );
2343 RenderYUVA( p_filter, p_region_out, p_lines,
2344 result.x, result.y );
2348 FreeLines( p_lines );
2350 xml_ReaderDelete( p_xml, p_xml_reader );
2352 xml_Delete( p_xml );
2354 stream_Delete( p_sub );
2360 static char* FontConfig_Select( FcConfig* priv, const char* family,
2361 bool b_bold, bool b_italic, int *i_idx )
2364 FcPattern *pat, *p_pat;
2368 pat = FcPatternCreate();
2369 if (!pat) return NULL;
2371 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2372 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2373 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2374 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2376 FcDefaultSubstitute( pat );
2378 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2380 FcPatternDestroy( pat );
2384 p_pat = FcFontMatch( priv, pat, &result );
2385 FcPatternDestroy( pat );
2386 if( !p_pat ) return NULL;
2388 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2389 || ( val_b != FcTrue ) )
2391 FcPatternDestroy( p_pat );
2394 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2399 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2401 FcPatternDestroy( p_pat );
2406 if( strcasecmp((const char*)val_s, family ) != 0 )
2407 msg_Warn( p_filter, "fontconfig: selected font family is not"
2408 "the requested one: '%s' != '%s'\n",
2409 (const char*)val_s, family );
2412 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2414 FcPatternDestroy( p_pat );
2418 FcPatternDestroy( p_pat );
2419 return strdup( (const char*)val_s );
2423 static void FreeLine( line_desc_t *p_line )
2426 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2428 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2430 free( p_line->pp_glyphs );
2431 free( p_line->p_glyph_pos );
2432 free( p_line->p_fg_rgb );
2433 free( p_line->p_bg_rgb );
2434 free( p_line->p_fg_bg_ratio );
2435 free( p_line->pi_underline_offset );
2436 free( p_line->pi_underline_thickness );
2440 static void FreeLines( line_desc_t *p_lines )
2442 line_desc_t *p_line, *p_next;
2444 if( !p_lines ) return;
2446 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2448 p_next = p_line->p_next;
2453 static line_desc_t *NewLine( int i_count )
2455 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2457 if( !p_line ) return NULL;
2458 p_line->i_height = 0;
2459 p_line->i_width = 0;
2460 p_line->p_next = NULL;
2462 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2463 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2464 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2465 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2466 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2467 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2468 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2469 if( ( p_line->pp_glyphs == NULL ) ||
2470 ( p_line->p_glyph_pos == NULL ) ||
2471 ( p_line->p_fg_rgb == NULL ) ||
2472 ( p_line->p_bg_rgb == NULL ) ||
2473 ( p_line->p_fg_bg_ratio == NULL ) ||
2474 ( p_line->pi_underline_offset == NULL ) ||
2475 ( p_line->pi_underline_thickness == NULL ) )
2477 free( p_line->pi_underline_thickness );
2478 free( p_line->pi_underline_offset );
2479 free( p_line->p_fg_rgb );
2480 free( p_line->p_bg_rgb );
2481 free( p_line->p_fg_bg_ratio );
2482 free( p_line->p_glyph_pos );
2483 free( p_line->pp_glyphs );
2487 p_line->pp_glyphs[0] = NULL;
2488 p_line->b_new_color_mode = false;
2493 static int GetFontSize( filter_t *p_filter )
2495 filter_sys_t *p_sys = p_filter->p_sys;
2499 if( p_sys->i_default_font_size )
2501 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2502 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2504 i_size = p_sys->i_default_font_size;
2508 var_Get( p_filter, "freetype-rel-fontsize", &val );
2511 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2512 p_filter->p_sys->i_display_height =
2513 p_filter->fmt_out.video.i_height;
2518 msg_Warn( p_filter, "invalid fontsize, using 12" );
2519 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2520 i_size = 12 * val.i_int / 1000;
2527 static int SetFontSize( filter_t *p_filter, int i_size )
2529 filter_sys_t *p_sys = p_filter->p_sys;
2533 i_size = GetFontSize( p_filter );
2535 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2538 p_sys->i_font_size = i_size;
2540 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2542 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2543 return VLC_EGENERIC;
2549 static void YUVFromRGB( uint32_t i_argb,
2550 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2552 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2553 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2554 int i_blue = ( i_argb & 0x000000ff );
2556 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2557 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2558 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2559 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2560 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2561 -585 * i_blue + 4096 + 1048576) >> 13, 240);