1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
42 #include <vlc_input.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
52 #define FT_MulFix(v, s) (((v)*(s))>>16)
55 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
56 #define FC_DEFAULT_FONT "Lucida Grande"
57 #elif defined( SYS_BEOS )
58 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
59 #define FC_DEFAULT_FONT "Swiss"
60 #elif defined( WIN32 )
61 #define DEFAULT_FONT "" /* Default font found at run-time */
62 #define FC_DEFAULT_FONT "Arial"
64 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
65 #define FC_DEFAULT_FONT "Serif Bold"
68 #if defined(HAVE_FRIBIDI)
69 #include <fribidi/fribidi.h>
72 #ifdef HAVE_FONTCONFIG
73 #include <fontconfig/fontconfig.h>
78 /*****************************************************************************
80 *****************************************************************************/
81 static int Create ( vlc_object_t * );
82 static void Destroy( vlc_object_t * );
84 #define FONT_TEXT N_("Font")
85 #define FONT_LONGTEXT N_("Filename for the font you want to use")
86 #define FONTSIZE_TEXT N_("Font size in pixels")
87 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
88 "that will be rendered on the video. " \
89 "If set to something different than 0 this option will override the " \
90 "relative font size." )
91 #define OPACITY_TEXT N_("Opacity")
92 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
93 "text that will be rendered on the video. 0 = transparent, " \
94 "255 = totally opaque. " )
95 #define COLOR_TEXT N_("Text default color")
96 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
97 "the video. This must be an hexadecimal (like HTML colors). The first two "\
98 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
99 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
100 #define FONTSIZER_TEXT N_("Relative font size")
101 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
102 "fonts that will be rendered on the video. If absolute font size is set, "\
103 "relative size will be overriden." )
105 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
106 static const char *const ppsz_sizes_text[] = {
107 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
108 #define YUVP_TEXT N_("Use YUVP renderer")
109 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
110 "This option is only needed if you want to encode into DVB subtitles" )
111 #define EFFECT_TEXT N_("Font Effect")
112 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
113 "text to improve its readability." )
115 #define EFFECT_BACKGROUND 1
116 #define EFFECT_OUTLINE 2
117 #define EFFECT_OUTLINE_FAT 3
119 static int const pi_effects[] = { 1, 2, 3 };
120 static const char *const ppsz_effects_text[] = {
121 N_("Background"),N_("Outline"), N_("Fat Outline") };
122 static const int pi_color_values[] = {
123 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
124 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
125 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
127 static const char *const ppsz_color_descriptions[] = {
128 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
129 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
130 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
133 set_shortname( N_("Text renderer"));
134 set_description( N_("Freetype2 font renderer") );
135 set_category( CAT_VIDEO );
136 set_subcategory( SUBCAT_VIDEO_SUBPIC );
138 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
141 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
142 FONTSIZE_LONGTEXT, true );
144 /* opacity valid on 0..255, with default 255 = fully opaque */
145 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
146 OPACITY_TEXT, OPACITY_LONGTEXT, true );
148 /* hook to the color values list, with default 0x00ffffff = white */
149 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
150 COLOR_LONGTEXT, false );
151 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
153 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
154 FONTSIZER_LONGTEXT, false );
155 change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
156 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
157 EFFECT_LONGTEXT, false );
158 change_integer_list( pi_effects, ppsz_effects_text, NULL );
160 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
161 YUVP_LONGTEXT, true );
162 set_capability( "text renderer", 100 );
163 add_shortcut( "text" );
164 set_callbacks( Create, Destroy );
169 /*****************************************************************************
171 *****************************************************************************/
173 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
174 static int RenderText( filter_t *, subpicture_region_t *,
175 subpicture_region_t * );
176 #ifdef HAVE_FONTCONFIG
177 static int RenderHtml( filter_t *, subpicture_region_t *,
178 subpicture_region_t * );
179 static char *FontConfig_Select( FcConfig *, const char *,
184 static int LoadFontsFromAttachments( filter_t *p_filter );
186 static int GetFontSize( filter_t *p_filter );
187 static int SetFontSize( filter_t *, int );
188 static void YUVFromRGB( uint32_t i_argb,
189 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
191 typedef struct line_desc_t line_desc_t;
194 /** NULL-terminated list of glyphs making the string */
195 FT_BitmapGlyph *pp_glyphs;
196 /** list of relative positions for the glyphs */
197 FT_Vector *p_glyph_pos;
198 /** list of RGB information for styled text
199 * -- if the rendering mode supports it (RenderYUVA) and
200 * b_new_color_mode is set, then it becomes possible to
201 * have multicoloured text within the subtitles. */
204 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
205 bool b_new_color_mode;
206 /** underline information -- only supplied if text should be underlined */
207 uint16_t *pi_underline_offset;
208 uint16_t *pi_underline_thickness;
212 int i_red, i_green, i_blue;
217 static line_desc_t *NewLine( int );
219 typedef struct font_stack_t font_stack_t;
224 uint32_t i_color; /* ARGB */
225 uint32_t i_karaoke_bg_color; /* ARGB */
227 font_stack_t *p_next;
233 uint32_t i_font_color; /* ARGB */
234 uint32_t i_karaoke_bg_color; /* ARGB */
241 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
242 static void FreeLines( line_desc_t * );
243 static void FreeLine( line_desc_t * );
245 #ifdef HAVE_FONTCONFIG
246 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock );
247 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
248 static void* FontBuilderThread( vlc_object_t *p_this);
249 static void FontBuilderDestructor( vlc_object_t *p_this );
250 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
251 static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
255 /*****************************************************************************
256 * filter_sys_t: freetype local data
257 *****************************************************************************
258 * This structure is part of the video output thread descriptor.
259 * It describes the freetype specific properties of an output thread.
260 *****************************************************************************/
263 FT_Library p_library; /* handle to library */
264 FT_Face p_face; /* handle to face object */
266 uint8_t i_font_opacity;
271 int i_default_font_size;
272 int i_display_height;
273 #ifdef HAVE_FONTCONFIG
274 vlc_mutex_t *p_fontconfig_lock;
275 bool b_fontconfig_ok;
276 FcConfig *p_fontconfig;
279 input_attachment_t **pp_font_attachments;
280 int i_font_attachments;
282 vlc_object_t *p_fontbuilder;
285 /*****************************************************************************
286 * Create: allocates osd-text video thread output method
287 *****************************************************************************
288 * This function allocates and initializes a Clone vout method.
289 *****************************************************************************/
290 static int Create( vlc_object_t *p_this )
292 filter_t *p_filter = (filter_t *)p_this;
294 char *psz_fontfile = NULL;
298 /* Allocate structure */
299 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
303 p_sys->p_library = 0;
304 p_sys->i_font_size = 0;
305 p_sys->i_display_height = 0;
307 var_Create( p_filter, "freetype-font",
308 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
309 var_Create( p_filter, "freetype-fontsize",
310 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
311 var_Create( p_filter, "freetype-rel-fontsize",
312 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
313 var_Create( p_filter, "freetype-opacity",
314 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
315 var_Create( p_filter, "freetype-effect",
316 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
317 var_Get( p_filter, "freetype-opacity", &val );
318 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
319 var_Create( p_filter, "freetype-color",
320 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
321 var_Get( p_filter, "freetype-color", &val );
322 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
323 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
325 /* Look what method was requested */
326 var_Get( p_filter, "freetype-font", &val );
327 psz_fontfile = val.psz_string;
328 if( !psz_fontfile || !*psz_fontfile )
330 free( psz_fontfile );
331 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
335 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
336 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
337 #elif defined(__APPLE__)
338 strcpy( psz_fontfile, DEFAULT_FONT );
340 msg_Err( p_filter, "user didn't specify a font" );
345 i_error = FT_Init_FreeType( &p_sys->p_library );
348 msg_Err( p_filter, "couldn't initialize freetype" );
351 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
353 if( i_error == FT_Err_Unknown_File_Format )
355 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
360 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
364 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
367 msg_Err( p_filter, "font has no unicode translation table" );
371 #ifdef HAVE_FONTCONFIG
372 p_sys->b_fontconfig_ok = false;
373 p_sys->p_fontconfig = NULL;
374 p_sys->p_fontbuilder = FontBuilderAttach( p_filter, &p_sys->p_fontconfig_lock );
377 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
379 var_Get( p_filter, "freetype-fontsize", &val );
380 p_sys->i_default_font_size = val.i_int;
381 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
383 free( psz_fontfile );
385 p_sys->pp_font_attachments = NULL;
386 p_sys->i_font_attachments = 0;
388 p_filter->pf_render_text = RenderText;
389 #ifdef HAVE_FONTCONFIG
390 p_filter->pf_render_html = RenderHtml;
392 p_filter->pf_render_html = NULL;
395 LoadFontsFromAttachments( p_filter );
400 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
401 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
402 free( psz_fontfile );
407 /*****************************************************************************
408 * Destroy: destroy Clone video thread output method
409 *****************************************************************************
410 * Clean up all data and library connections
411 *****************************************************************************/
412 static void Destroy( vlc_object_t *p_this )
414 filter_t *p_filter = (filter_t *)p_this;
415 filter_sys_t *p_sys = p_filter->p_sys;
417 if( p_sys->pp_font_attachments )
421 for( k = 0; k < p_sys->i_font_attachments; k++ )
422 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
424 free( p_sys->pp_font_attachments );
427 #ifdef HAVE_FONTCONFIG
428 FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
431 /* FcFini asserts calling the subfunction FcCacheFini()
432 * even if no other library functions have been made since FcInit(),
433 * so don't call it. */
435 FT_Done_Face( p_sys->p_face );
436 FT_Done_FreeType( p_sys->p_library );
440 #ifdef HAVE_FONTCONFIG
441 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock )
443 /* Check for an existing Fontbuilder thread */
444 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
445 vlc_object_t *p_fontbuilder =
446 vlc_object_find_name( p_filter->p_libvlc,
447 "fontlist builder", FIND_CHILD );
451 /* Create the FontBuilderThread thread as a child of a top-level
452 * object, so that it can survive the destruction of the
453 * freetype object - the fontlist only needs to be built once,
454 * and calling the fontbuild a second time while the first is
455 * still in progress can cause thread instabilities.
457 * XXX The fontbuilder will be destroy as soon as it is unused.
460 p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
461 sizeof(vlc_object_t) );
464 p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
465 p_fontbuilder->p_private = NULL;
466 vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
468 vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
470 var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
471 var_SetBool( p_fontbuilder, "build-done", false );
473 if( vlc_thread_create( p_fontbuilder,
476 VLC_THREAD_PRIORITY_LOW,
479 msg_Warn( p_filter, "fontconfig database builder thread can't "
480 "be launched. Font styling support will be limited." );
486 var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
487 FontBuilderGetFcConfig( p_filter, p_fontbuilder );
489 vlc_mutex_unlock( p_lock );
491 return p_fontbuilder;
493 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
495 vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
498 const bool b_alive = vlc_object_alive( p_fontbuilder );
500 var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
502 /* We wait for the thread on the first FontBuilderDetach */
505 vlc_object_kill( p_fontbuilder );
506 vlc_mutex_unlock( lock );
508 /* We need to unlock otherwise we may not join (the thread waiting
509 * for the lock). It is safe to unlock as no one else will try a
510 * join and we have a reference on the object) */
511 vlc_thread_join( p_fontbuilder );
513 vlc_mutex_lock( lock );
515 vlc_object_release( p_fontbuilder );
517 vlc_mutex_unlock( lock );
519 static void* FontBuilderThread( vlc_object_t *p_this )
521 FcConfig *p_fontconfig = FcInitLoadConfig();
523 vlc_thread_ready( p_this );
529 //msg_Dbg( p_this, "Building font database..." );
530 msg_Dbg( p_this, "Building font database..." );
532 if(! FcConfigBuildFonts( p_fontconfig ))
534 /* Don't destroy the fontconfig object - we won't be able to do
535 * italics or bold or change the font face, but we will still
536 * be able to do underline and change the font size.
538 msg_Err( p_this, "fontconfig database can't be built. "
539 "Font styling won't be available" );
543 msg_Dbg( p_this, "Finished building font database." );
544 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
546 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
547 p_this->p_private = p_fontconfig;
548 vlc_mutex_unlock( p_lock );
550 var_SetBool( p_this, "build-done", true );
554 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
556 filter_sys_t *p_sys = p_filter->p_sys;
558 p_sys->p_fontconfig = p_fontbuilder->p_private;
559 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
561 static void FontBuilderDestructor( vlc_object_t *p_this )
563 FcConfig *p_fontconfig = p_this->p_private;
566 FcConfigDestroy( p_fontconfig );
568 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
569 vlc_value_t oldval, vlc_value_t newval, void *param )
571 filter_t *p_filter = param;
575 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
577 FontBuilderGetFcConfig( p_filter, p_this );
579 vlc_mutex_unlock( p_lock );
588 /*****************************************************************************
589 * Make any TTF/OTF fonts present in the attachments of the media file
590 * and store them for later use by the FreeType Engine
591 *****************************************************************************/
592 static int LoadFontsFromAttachments( filter_t *p_filter )
594 filter_sys_t *p_sys = p_filter->p_sys;
595 input_thread_t *p_input;
596 input_attachment_t **pp_attachments;
597 int i_attachments_cnt;
599 int rv = VLC_SUCCESS;
601 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
605 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
607 vlc_object_release(p_input);
611 p_sys->i_font_attachments = 0;
612 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
613 if(! p_sys->pp_font_attachments )
616 for( k = 0; k < i_attachments_cnt; k++ )
618 input_attachment_t *p_attach = pp_attachments[k];
620 if( p_sys->pp_font_attachments )
622 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
623 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
624 ( p_attach->i_data > 0 ) &&
625 ( p_attach->p_data != NULL ) )
627 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
631 vlc_input_attachment_Delete( p_attach );
636 vlc_input_attachment_Delete( p_attach );
639 free( pp_attachments );
641 vlc_object_release(p_input);
646 /*****************************************************************************
647 * Render: place string in picture
648 *****************************************************************************
649 * This function merges the previously rendered freetype glyphs into a picture
650 *****************************************************************************/
651 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
652 line_desc_t *p_line, int i_width, int i_height )
654 static const uint8_t pi_gamma[16] =
655 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
656 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
660 int i, x, y, i_pitch;
661 uint8_t i_y; /* YUV values, derived from incoming RGB */
663 subpicture_region_t *p_region_tmp;
665 /* Create a new subpicture region */
666 memset( &fmt, 0, sizeof(video_format_t) );
667 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
669 fmt.i_width = fmt.i_visible_width = i_width + 4;
670 fmt.i_height = fmt.i_visible_height = i_height + 4;
671 if( p_region->fmt.i_visible_width > 0 )
672 fmt.i_visible_width = p_region->fmt.i_visible_width;
673 if( p_region->fmt.i_visible_height > 0 )
674 fmt.i_visible_height = p_region->fmt.i_visible_height;
675 fmt.i_x_offset = fmt.i_y_offset = 0;
676 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
679 msg_Err( p_filter, "cannot allocate SPU region" );
683 p_region->fmt = p_region_tmp->fmt;
684 p_region->picture = p_region_tmp->picture;
685 free( p_region_tmp );
687 /* Calculate text color components */
688 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
689 25 * p_line->i_blue + 128) >> 8) + 16;
690 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
691 112 * p_line->i_blue + 128) >> 8) + 128;
692 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
693 18 * p_line->i_blue + 128) >> 8) + 128;
696 fmt.p_palette->i_entries = 16;
697 for( i = 0; i < 8; i++ )
699 fmt.p_palette->palette[i][0] = 0;
700 fmt.p_palette->palette[i][1] = 0x80;
701 fmt.p_palette->palette[i][2] = 0x80;
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;
706 for( i = 8; i < fmt.p_palette->i_entries; i++ )
708 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
709 fmt.p_palette->palette[i][1] = i_u;
710 fmt.p_palette->palette[i][2] = i_v;
711 fmt.p_palette->palette[i][3] = pi_gamma[i];
712 fmt.p_palette->palette[i][3] =
713 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
716 p_dst = p_region->picture.Y_PIXELS;
717 i_pitch = p_region->picture.Y_PITCH;
719 /* Initialize the region pixels */
720 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
722 for( ; p_line != NULL; p_line = p_line->p_next )
724 int i_glyph_tmax = 0;
725 int i_bitmap_offset, i_offset, i_align_offset = 0;
726 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
728 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
729 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
732 if( p_line->i_width < i_width )
734 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
736 i_align_offset = i_width - p_line->i_width;
738 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
740 i_align_offset = ( i_width - p_line->i_width ) / 2;
744 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
746 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
748 i_offset = ( p_line->p_glyph_pos[ i ].y +
749 i_glyph_tmax - p_glyph->top + 2 ) *
750 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
753 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
755 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
757 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
759 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
766 /* Outlining (find something better than nearest neighbour filtering ?) */
769 uint8_t *p_dst = p_region->picture.Y_PIXELS;
770 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
771 uint8_t left, current;
773 for( y = 1; y < (int)fmt.i_height - 1; y++ )
775 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
776 p_dst += p_region->picture.Y_PITCH;
779 for( x = 1; x < (int)fmt.i_width - 1; x++ )
782 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
783 p_dst[x -1 + p_region->picture.Y_PITCH ] + p_dst[x + p_region->picture.Y_PITCH] + p_dst[x + 1 + p_region->picture.Y_PITCH]) / 16;
787 memset( p_top, 0, fmt.i_width );
793 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
794 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
795 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
796 int i_glyph_tmax, int i_align_offset,
797 uint8_t i_y, uint8_t i_u, uint8_t i_v,
798 subpicture_region_t *p_region)
802 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
804 p_dst_y = p_region->picture.Y_PIXELS;
805 p_dst_u = p_region->picture.U_PIXELS;
806 p_dst_v = p_region->picture.V_PIXELS;
807 p_dst_a = p_region->picture.A_PIXELS;
808 i_pitch = p_region->picture.A_PITCH;
810 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
811 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
813 for( y = 0; y < i_line_thickness; y++ )
815 int i_extra = p_this_glyph->bitmap.width;
819 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
820 (p_this_glyph_pos->x + p_this_glyph->left);
822 for( x = 0; x < i_extra; x++ )
826 /* break the underline around the tails of any glyphs which cross it */
827 for( z = x - i_line_thickness;
828 z < x + i_line_thickness && b_ok;
831 if( p_next_glyph && ( z >= i_extra ) )
833 int i_row = i_line_offset + p_next_glyph->top + y;
835 if( ( p_next_glyph->bitmap.rows > i_row ) &&
836 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
841 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
843 int i_row = i_line_offset + p_this_glyph->top + y;
845 if( ( p_this_glyph->bitmap.rows > i_row ) &&
846 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
855 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
856 p_dst_u[i_offset+x] = i_u;
857 p_dst_v[i_offset+x] = i_v;
858 p_dst_a[i_offset+x] = 255;
865 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
867 uint8_t *p_dst = p_region->picture.A_PIXELS;
868 int i_pitch = p_region->picture.A_PITCH;
871 for( ; p_line != NULL; p_line = p_line->p_next )
873 int i_glyph_tmax=0, i = 0;
874 int i_bitmap_offset, i_offset, i_align_offset = 0;
875 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
877 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
878 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
881 if( p_line->i_width < i_width )
883 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
885 i_align_offset = i_width - p_line->i_width;
887 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
889 i_align_offset = ( i_width - p_line->i_width ) / 2;
893 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
895 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
897 i_offset = ( p_line->p_glyph_pos[ i ].y +
898 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
899 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
900 i_align_offset +xoffset;
902 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
904 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
906 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
907 if( p_dst[i_offset+x] <
908 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
910 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
919 /*****************************************************************************
920 * Render: place string in picture
921 *****************************************************************************
922 * This function merges the previously rendered freetype glyphs into a picture
923 *****************************************************************************/
924 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
925 line_desc_t *p_line, int i_width, int i_height )
927 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
929 int i, x, y, i_pitch, i_alpha;
930 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
931 subpicture_region_t *p_region_tmp;
933 if( i_width == 0 || i_height == 0 )
936 /* Create a new subpicture region */
937 memset( &fmt, 0, sizeof(video_format_t) );
938 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
940 fmt.i_width = fmt.i_visible_width = i_width + 6;
941 fmt.i_height = fmt.i_visible_height = i_height + 6;
942 if( p_region->fmt.i_visible_width > 0 )
943 fmt.i_visible_width = p_region->fmt.i_visible_width;
944 if( p_region->fmt.i_visible_height > 0 )
945 fmt.i_visible_height = p_region->fmt.i_visible_height;
946 fmt.i_x_offset = fmt.i_y_offset = 0;
947 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
950 msg_Err( p_filter, "cannot allocate SPU region" );
954 p_region->fmt = p_region_tmp->fmt;
955 p_region->picture = p_region_tmp->picture;
956 free( p_region_tmp );
958 /* Calculate text color components */
959 YUVFromRGB( (p_line->i_red << 16) |
960 (p_line->i_green << 8) |
963 i_alpha = p_line->i_alpha;
965 p_dst_y = p_region->picture.Y_PIXELS;
966 p_dst_u = p_region->picture.U_PIXELS;
967 p_dst_v = p_region->picture.V_PIXELS;
968 p_dst_a = p_region->picture.A_PIXELS;
969 i_pitch = p_region->picture.A_PITCH;
971 /* Initialize the region pixels */
972 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
974 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
975 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
976 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
977 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
981 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
982 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
983 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
984 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
986 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
987 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
989 DrawBlack( p_line, i_width, p_region, 0, 0);
990 DrawBlack( p_line, i_width, p_region, -1, 0);
991 DrawBlack( p_line, i_width, p_region, 0, -1);
992 DrawBlack( p_line, i_width, p_region, 1, 0);
993 DrawBlack( p_line, i_width, p_region, 0, 1);
996 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
998 DrawBlack( p_line, i_width, p_region, -1, -1);
999 DrawBlack( p_line, i_width, p_region, -1, 1);
1000 DrawBlack( p_line, i_width, p_region, 1, -1);
1001 DrawBlack( p_line, i_width, p_region, 1, 1);
1003 DrawBlack( p_line, i_width, p_region, -2, 0);
1004 DrawBlack( p_line, i_width, p_region, 0, -2);
1005 DrawBlack( p_line, i_width, p_region, 2, 0);
1006 DrawBlack( p_line, i_width, p_region, 0, 2);
1008 DrawBlack( p_line, i_width, p_region, -2, -2);
1009 DrawBlack( p_line, i_width, p_region, -2, 2);
1010 DrawBlack( p_line, i_width, p_region, 2, -2);
1011 DrawBlack( p_line, i_width, p_region, 2, 2);
1013 DrawBlack( p_line, i_width, p_region, -3, 0);
1014 DrawBlack( p_line, i_width, p_region, 0, -3);
1015 DrawBlack( p_line, i_width, p_region, 3, 0);
1016 DrawBlack( p_line, i_width, p_region, 0, 3);
1019 for( ; p_line != NULL; p_line = p_line->p_next )
1021 int i_glyph_tmax = 0;
1022 int i_bitmap_offset, i_offset, i_align_offset = 0;
1023 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1025 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1026 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1029 if( p_line->i_width < i_width )
1031 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1033 i_align_offset = i_width - p_line->i_width;
1035 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1037 i_align_offset = ( i_width - p_line->i_width ) / 2;
1041 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1043 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1045 i_offset = ( p_line->p_glyph_pos[ i ].y +
1046 i_glyph_tmax - p_glyph->top + 3 ) *
1047 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1050 if( p_line->b_new_color_mode )
1052 /* Every glyph can (and in fact must) have its own color */
1053 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1056 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1058 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1060 uint8_t i_y_local = i_y;
1061 uint8_t i_u_local = i_u;
1062 uint8_t i_v_local = i_v;
1064 if( p_line->p_fg_bg_ratio != 0x00 )
1066 int i_split = p_glyph->bitmap.width *
1067 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1071 YUVFromRGB( p_line->p_bg_rgb[ i ],
1072 &i_y_local, &i_u_local, &i_v_local );
1076 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1078 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1079 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1081 p_dst_u[i_offset+x] = i_u;
1082 p_dst_v[i_offset+x] = i_v;
1084 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1085 p_dst_a[i_offset+x] = 0xff;
1088 i_offset += i_pitch;
1091 if( p_line->pi_underline_thickness[ i ] )
1093 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1094 p_line->pi_underline_offset[ i ],
1095 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1096 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1097 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1098 i_glyph_tmax, i_align_offset,
1105 /* Apply the alpha setting */
1106 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1107 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1113 * This function renders a text subpicture region into another one.
1114 * It also calculates the size needed for this string, and renders the
1115 * needed glyphs into memory. It is used as pf_add_string callback in
1116 * the vout method by this module
1118 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1119 subpicture_region_t *p_region_in )
1121 filter_sys_t *p_sys = p_filter->p_sys;
1122 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1123 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1124 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1125 int i_string_length;
1127 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1128 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1138 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1139 psz_string = p_region_in->psz_text;
1140 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1142 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1143 i_scale = val.i_int;
1145 if( p_region_in->p_style )
1147 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1148 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1149 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1153 i_font_color = p_sys->i_font_color;
1154 i_font_alpha = 255 - p_sys->i_font_opacity;
1155 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1158 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1159 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1160 SetFontSize( p_filter, i_font_size );
1162 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1163 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1164 i_blue = i_font_color & 0x000000FF;
1166 result.x = result.y = 0;
1167 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1169 psz_unicode = psz_unicode_orig =
1170 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1171 if( psz_unicode == NULL )
1173 #if defined(WORDS_BIGENDIAN)
1174 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1176 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1178 if( iconv_handle == (vlc_iconv_t)-1 )
1180 msg_Warn( p_filter, "unable to do conversion" );
1186 const char *p_in_buffer = psz_string;
1187 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1188 i_in_bytes = strlen( psz_string );
1189 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1190 i_out_bytes_left = i_out_bytes;
1191 p_out_buffer = (char *)psz_unicode;
1192 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1194 &p_out_buffer, &i_out_bytes_left );
1196 vlc_iconv_close( iconv_handle );
1200 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1201 "bytes left %u", (unsigned)i_in_bytes );
1204 *(uint32_t*)p_out_buffer = 0;
1205 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1208 #if defined(HAVE_FRIBIDI)
1210 uint32_t *p_fribidi_string;
1211 int32_t start_pos, pos = 0;
1213 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1214 if( !p_fribidi_string )
1217 /* Do bidi conversion line-by-line */
1218 while( pos < i_string_length )
1220 while( pos < i_string_length )
1222 i_char = psz_unicode[pos];
1223 if (i_char != '\r' && i_char != '\n')
1225 p_fribidi_string[pos] = i_char;
1229 while( pos < i_string_length )
1231 i_char = psz_unicode[pos];
1232 if (i_char == '\r' || i_char == '\n')
1236 if (pos > start_pos)
1238 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1239 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1242 (FriBidiChar*)p_fribidi_string + start_pos,
1247 free( psz_unicode_orig );
1248 psz_unicode = psz_unicode_orig = p_fribidi_string;
1249 p_fribidi_string[ i_string_length ] = 0;
1253 /* Calculate relative glyph positions and a bounding box for the
1255 if( !(p_line = NewLine( strlen( psz_string ))) )
1258 i_pen_x = i_pen_y = 0;
1260 psz_line_start = psz_unicode;
1262 #define face p_sys->p_face
1263 #define glyph face->glyph
1265 while( *psz_unicode )
1267 i_char = *psz_unicode++;
1268 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1273 if( i_char == '\n' )
1275 psz_line_start = psz_unicode;
1276 if( !(p_next = NewLine( strlen( psz_string ))) )
1278 p_line->p_next = p_next;
1279 p_line->i_width = line.xMax;
1280 p_line->i_height = face->size->metrics.height >> 6;
1281 p_line->pp_glyphs[ i ] = NULL;
1282 p_line->i_alpha = i_font_alpha;
1283 p_line->i_red = i_red;
1284 p_line->i_green = i_green;
1285 p_line->i_blue = i_blue;
1288 result.x = __MAX( result.x, line.xMax );
1289 result.y += face->size->metrics.height >> 6;
1292 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1293 i_pen_y += face->size->metrics.height >> 6;
1295 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1300 i_glyph_index = FT_Get_Char_Index( face, i_char );
1301 if( p_sys->i_use_kerning && i_glyph_index
1305 FT_Get_Kerning( face, i_previous, i_glyph_index,
1306 ft_kerning_default, &delta );
1307 i_pen_x += delta.x >> 6;
1310 p_line->p_glyph_pos[ i ].x = i_pen_x;
1311 p_line->p_glyph_pos[ i ].y = i_pen_y;
1312 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1315 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1319 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1322 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1326 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1327 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1330 FT_Done_Glyph( tmp_glyph );
1333 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1336 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1337 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1338 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1340 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1341 p_line->pp_glyphs[ i ] = NULL;
1343 p_line = NewLine( strlen( psz_string ));
1344 if( p_prev ) p_prev->p_next = p_line;
1345 else p_lines = p_line;
1347 uint32_t *psz_unicode_saved = psz_unicode;
1348 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1352 if( psz_unicode == psz_line_start )
1353 { /* try harder to break that line */
1354 psz_unicode = psz_unicode_saved;
1355 while( psz_unicode > psz_line_start &&
1356 *psz_unicode != '_' && *psz_unicode != '/' &&
1357 *psz_unicode != '\\' && *psz_unicode != '.' )
1362 if( psz_unicode == psz_line_start )
1364 msg_Warn( p_filter, "unbreakable string" );
1369 *psz_unicode = '\n';
1371 psz_unicode = psz_line_start;
1374 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1377 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1378 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1380 i_previous = i_glyph_index;
1381 i_pen_x += glyph->advance.x >> 6;
1385 p_line->i_width = line.xMax;
1386 p_line->i_height = face->size->metrics.height >> 6;
1387 p_line->pp_glyphs[ i ] = NULL;
1388 p_line->i_alpha = i_font_alpha;
1389 p_line->i_red = i_red;
1390 p_line->i_green = i_green;
1391 p_line->i_blue = i_blue;
1392 result.x = __MAX( result.x, line.xMax );
1393 result.y += line.yMax - line.yMin;
1398 p_region_out->i_x = p_region_in->i_x;
1399 p_region_out->i_y = p_region_in->i_y;
1401 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1402 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1404 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1406 free( psz_unicode_orig );
1407 FreeLines( p_lines );
1411 free( psz_unicode_orig );
1412 FreeLines( p_lines );
1413 return VLC_EGENERIC;
1416 #ifdef HAVE_FONTCONFIG
1417 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1418 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1419 bool b_italic, bool b_uline )
1421 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1425 p_style->i_font_size = i_font_size;
1426 p_style->i_font_color = i_font_color;
1427 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1428 p_style->b_italic = b_italic;
1429 p_style->b_bold = b_bold;
1430 p_style->b_underline = b_uline;
1432 p_style->psz_fontname = strdup( psz_fontname );
1437 static void DeleteStyle( ft_style_t *p_style )
1441 free( p_style->psz_fontname );
1446 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1453 if(( s1->i_font_size == s2->i_font_size ) &&
1454 ( s1->i_font_color == s2->i_font_color ) &&
1455 ( s1->b_italic == s2->b_italic ) &&
1456 ( s1->b_bold == s2->b_bold ) &&
1457 ( s1->b_underline == s2->b_underline ) &&
1458 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1465 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1466 uint32_t i_color, uint32_t i_karaoke_bg_color )
1468 font_stack_t *p_new;
1471 return VLC_EGENERIC;
1473 p_new = malloc( sizeof( font_stack_t ) );
1477 p_new->p_next = NULL;
1480 p_new->psz_name = strdup( psz_name );
1482 p_new->psz_name = NULL;
1484 p_new->i_size = i_size;
1485 p_new->i_color = i_color;
1486 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1494 font_stack_t *p_last;
1496 for( p_last = *p_font;
1498 p_last = p_last->p_next )
1501 p_last->p_next = p_new;
1506 static int PopFont( font_stack_t **p_font )
1508 font_stack_t *p_last, *p_next_to_last;
1510 if( !p_font || !*p_font )
1511 return VLC_EGENERIC;
1513 p_next_to_last = NULL;
1514 for( p_last = *p_font;
1516 p_last = p_last->p_next )
1518 p_next_to_last = p_last;
1521 if( p_next_to_last )
1522 p_next_to_last->p_next = NULL;
1526 free( p_last->psz_name );
1532 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1533 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1535 font_stack_t *p_last;
1537 if( !p_font || !*p_font )
1538 return VLC_EGENERIC;
1540 for( p_last=*p_font;
1542 p_last=p_last->p_next )
1545 *psz_name = p_last->psz_name;
1546 *i_size = p_last->i_size;
1547 *i_color = p_last->i_color;
1548 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1553 static void IconvText( filter_t *p_filter, const char *psz_string,
1554 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1556 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1558 /* If memory hasn't been allocated for our output string, allocate it here
1559 * - the calling function must now be responsible for freeing it.
1561 if( !*ppsz_unicode )
1562 *ppsz_unicode = (uint32_t *)
1563 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1565 /* We don't need to handle a NULL pointer in *ppsz_unicode
1566 * if we are instead testing for a non NULL value like we are here */
1570 #if defined(WORDS_BIGENDIAN)
1571 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1573 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1575 if( iconv_handle != (vlc_iconv_t)-1 )
1577 char *p_in_buffer, *p_out_buffer;
1578 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1579 i_in_bytes = strlen( psz_string );
1580 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1581 i_out_bytes_left = i_out_bytes;
1582 p_in_buffer = (char *) psz_string;
1583 p_out_buffer = (char *) *ppsz_unicode;
1584 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1585 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1587 vlc_iconv_close( iconv_handle );
1591 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1592 "bytes left %u", (unsigned)i_in_bytes );
1596 *(uint32_t*)p_out_buffer = 0;
1598 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1603 msg_Warn( p_filter, "unable to do conversion" );
1608 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1609 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1612 ft_style_t *p_style = NULL;
1614 char *psz_fontname = NULL;
1615 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1616 uint32_t i_karaoke_bg_color = i_font_color;
1617 int i_font_size = p_sys->i_font_size;
1619 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1620 &i_font_color, &i_karaoke_bg_color ))
1622 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1623 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1628 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1629 bool b_uline, int i_karaoke_bgcolor,
1630 line_desc_t *p_line, uint32_t *psz_unicode,
1631 int *pi_pen_x, int i_pen_y, int *pi_start,
1632 FT_Vector *p_result )
1637 bool b_first_on_line = true;
1640 int i_pen_x_start = *pi_pen_x;
1642 uint32_t *psz_unicode_start = psz_unicode;
1644 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1646 /* Account for part of line already in position */
1647 for( i=0; i<*pi_start; i++ )
1651 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1652 ft_glyph_bbox_pixels, &glyph_size );
1654 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1655 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1656 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1657 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1663 b_first_on_line = false;
1665 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1671 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1672 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1676 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1677 ft_kerning_default, &delta );
1678 *pi_pen_x += delta.x >> 6;
1680 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1681 p_line->p_glyph_pos[ i ].y = i_pen_y;
1683 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1687 "unable to render text FT_Load_Glyph returned %d", i_error );
1688 p_line->pp_glyphs[ i ] = NULL;
1689 return VLC_EGENERIC;
1691 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1695 "unable to render text FT_Get_Glyph returned %d", i_error );
1696 p_line->pp_glyphs[ i ] = NULL;
1697 return VLC_EGENERIC;
1699 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1700 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1703 FT_Done_Glyph( tmp_glyph );
1708 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1709 p_face->size->metrics.y_scale));
1710 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1711 p_face->size->metrics.y_scale));
1713 p_line->pi_underline_offset[ i ] =
1714 ( aOffset < 0 ) ? -aOffset : aOffset;
1715 p_line->pi_underline_thickness[ i ] =
1716 ( aSize < 0 ) ? -aSize : aSize;
1718 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1719 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1720 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1721 p_line->p_fg_bg_ratio[ i ] = 0x00;
1723 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1724 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1725 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1727 while( --i > *pi_start )
1729 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1732 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1736 if( psz_unicode == psz_unicode_start )
1738 if( b_first_on_line )
1740 msg_Warn( p_filter, "unbreakable string" );
1741 p_line->pp_glyphs[ i ] = NULL;
1742 return VLC_EGENERIC;
1744 *pi_pen_x = i_pen_x_start;
1746 p_line->i_width = line.xMax;
1747 p_line->i_height = __MAX( p_line->i_height,
1748 p_face->size->metrics.height >> 6 );
1749 p_line->pp_glyphs[ i ] = NULL;
1751 p_result->x = __MAX( p_result->x, line.xMax );
1752 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1753 i_yMax - i_yMin ) );
1760 *psz_unicode = '\n';
1762 psz_unicode = psz_unicode_start;
1763 *pi_pen_x = i_pen_x_start;
1771 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1772 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1774 i_previous = i_glyph_index;
1775 *pi_pen_x += p_face->glyph->advance.x >> 6;
1778 p_line->i_width = line.xMax;
1779 p_line->i_height = __MAX( p_line->i_height,
1780 p_face->size->metrics.height >> 6 );
1781 p_line->pp_glyphs[ i ] = NULL;
1783 p_result->x = __MAX( p_result->x, line.xMax );
1784 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1785 line.yMax - line.yMin ) );
1789 /* Get rid of any text processed - if necessary repositioning
1790 * at the start of a new line of text
1794 *psz_unicode_start = '\0';
1796 else if( psz_unicode > psz_unicode_start )
1798 for( i=0; psz_unicode[ i ]; i++ )
1799 psz_unicode_start[ i ] = psz_unicode[ i ];
1800 psz_unicode_start[ i ] = '\0';
1806 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1807 font_stack_t **p_fonts, int i_scale )
1810 char *psz_fontname = NULL;
1811 uint32_t i_font_color = 0xffffff;
1812 int i_font_alpha = 0;
1813 uint32_t i_karaoke_bg_color = 0x00ffffff;
1814 int i_font_size = 24;
1816 /* Default all attributes to the top font in the stack -- in case not
1817 * all attributes are specified in the sub-font
1819 if( VLC_SUCCESS == PeekFont( p_fonts,
1823 &i_karaoke_bg_color ))
1825 psz_fontname = strdup( psz_fontname );
1826 i_font_size = i_font_size * 1000 / i_scale;
1828 i_font_alpha = (i_font_color >> 24) & 0xff;
1829 i_font_color &= 0x00ffffff;
1831 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1833 char *psz_name = xml_ReaderName( p_xml_reader );
1834 char *psz_value = xml_ReaderValue( p_xml_reader );
1836 if( psz_name && psz_value )
1838 if( !strcasecmp( "face", psz_name ) )
1840 free( psz_fontname );
1841 psz_fontname = strdup( psz_value );
1843 else if( !strcasecmp( "size", psz_name ) )
1845 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1847 int i_value = atoi( psz_value );
1849 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1850 i_font_size += ( i_value * i_font_size ) / 10;
1851 else if( i_value < -5 )
1852 i_font_size = - i_value;
1853 else if( i_value > 5 )
1854 i_font_size = i_value;
1857 i_font_size = atoi( psz_value );
1859 else if( !strcasecmp( "color", psz_name ) &&
1860 ( psz_value[0] == '#' ) )
1862 i_font_color = strtol( psz_value + 1, NULL, 16 );
1863 i_font_color &= 0x00ffffff;
1865 else if( !strcasecmp( "alpha", psz_name ) &&
1866 ( psz_value[0] == '#' ) )
1868 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1869 i_font_alpha &= 0xff;
1875 rv = PushFont( p_fonts,
1877 i_font_size * i_scale / 1000,
1878 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1879 i_karaoke_bg_color );
1881 free( psz_fontname );
1886 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1887 uint32_t **psz_text_out, uint32_t *pi_runs,
1888 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1889 ft_style_t *p_style )
1891 uint32_t i_string_length = 0;
1893 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1894 *psz_text_out += i_string_length;
1896 if( ppp_styles && ppi_run_lengths )
1902 *ppp_styles = (ft_style_t **)
1903 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1905 else if( *pi_runs == 1 )
1907 *ppp_styles = (ft_style_t **)
1908 malloc( *pi_runs * sizeof( ft_style_t * ) );
1911 /* We have just malloc'ed this memory successfully -
1912 * *pi_runs HAS to be within the memory area of *ppp_styles */
1915 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1919 if( *ppi_run_lengths )
1921 *ppi_run_lengths = (uint32_t *)
1922 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1924 else if( *pi_runs == 1 )
1926 *ppi_run_lengths = (uint32_t *)
1927 malloc( *pi_runs * sizeof( uint32_t ) );
1930 /* same remarks here */
1931 if( *ppi_run_lengths )
1933 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1936 /* If we couldn't use the p_style argument due to memory allocation
1937 * problems above, release it here.
1939 if( p_style ) DeleteStyle( p_style );
1942 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1943 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1945 /* Karaoke tags _PRECEDE_ the text they specify a duration
1946 * for, therefore we are working out the length for the
1947 * previous tag, and first time through we have nothing
1949 if( pi_k_run_lengths )
1954 /* Work out how many characters are presently in the string
1956 for( i = 0; i < i_runs; i++ )
1957 i_chars += pi_run_lengths[ i ];
1959 /* Subtract away those we've already allocated to other
1962 for( i = 0; i < i_k_runs; i++ )
1963 i_chars -= pi_k_run_lengths[ i ];
1965 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1969 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1970 uint32_t **ppi_k_run_lengths,
1971 uint32_t **ppi_k_durations )
1973 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1975 char *psz_name = xml_ReaderName( p_xml_reader );
1976 char *psz_value = xml_ReaderValue( p_xml_reader );
1978 if( psz_name && psz_value &&
1979 !strcasecmp( "t", psz_name ) )
1981 if( ppi_k_durations && ppi_k_run_lengths )
1985 if( *ppi_k_durations )
1987 *ppi_k_durations = (uint32_t *)
1988 realloc( *ppi_k_durations,
1989 *pi_k_runs * sizeof( uint32_t ) );
1991 else if( *pi_k_runs == 1 )
1993 *ppi_k_durations = (uint32_t *)
1994 malloc( *pi_k_runs * sizeof( uint32_t ) );
1997 if( *ppi_k_run_lengths )
1999 *ppi_k_run_lengths = (uint32_t *)
2000 realloc( *ppi_k_run_lengths,
2001 *pi_k_runs * sizeof( uint32_t ) );
2003 else if( *pi_k_runs == 1 )
2005 *ppi_k_run_lengths = (uint32_t *)
2006 malloc( *pi_k_runs * sizeof( uint32_t ) );
2008 if( *ppi_k_durations )
2009 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
2011 if( *ppi_k_run_lengths )
2012 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
2020 static int ProcessNodes( filter_t *p_filter,
2021 xml_reader_t *p_xml_reader,
2022 text_style_t *p_font_style,
2027 uint32_t **ppi_run_lengths,
2028 ft_style_t ***ppp_styles,
2031 uint32_t *pi_k_runs,
2032 uint32_t **ppi_k_run_lengths,
2033 uint32_t **ppi_k_durations )
2035 int rv = VLC_SUCCESS;
2036 filter_sys_t *p_sys = p_filter->p_sys;
2037 uint32_t *psz_text_orig = psz_text;
2038 font_stack_t *p_fonts = NULL;
2042 char *psz_node = NULL;
2044 bool b_italic = false;
2045 bool b_bold = false;
2046 bool b_uline = false;
2048 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2049 i_scale = val.i_int;
2053 rv = PushFont( &p_fonts,
2054 p_font_style->psz_fontname,
2055 p_font_style->i_font_size * i_scale / 1000,
2056 (p_font_style->i_font_color & 0xffffff) |
2057 ((p_font_style->i_font_alpha & 0xff) << 24),
2058 (p_font_style->i_karaoke_background_color & 0xffffff) |
2059 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2061 if( p_font_style->i_style_flags & STYLE_BOLD )
2063 if( p_font_style->i_style_flags & STYLE_ITALIC )
2065 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2070 rv = PushFont( &p_fonts,
2076 if( rv != VLC_SUCCESS )
2079 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2081 switch ( xml_ReaderNodeType( p_xml_reader ) )
2083 case XML_READER_NONE:
2085 case XML_READER_ENDELEM:
2086 psz_node = xml_ReaderName( p_xml_reader );
2090 if( !strcasecmp( "font", psz_node ) )
2091 PopFont( &p_fonts );
2092 else if( !strcasecmp( "b", psz_node ) )
2094 else if( !strcasecmp( "i", psz_node ) )
2096 else if( !strcasecmp( "u", psz_node ) )
2102 case XML_READER_STARTELEM:
2103 psz_node = xml_ReaderName( p_xml_reader );
2106 if( !strcasecmp( "font", psz_node ) )
2107 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2108 else if( !strcasecmp( "b", psz_node ) )
2110 else if( !strcasecmp( "i", psz_node ) )
2112 else if( !strcasecmp( "u", psz_node ) )
2114 else if( !strcasecmp( "br", psz_node ) )
2116 SetupLine( p_filter, "\n", &psz_text,
2117 pi_runs, ppi_run_lengths, ppp_styles,
2118 GetStyleFromFontStack( p_sys,
2124 else if( !strcasecmp( "k", psz_node ) )
2126 /* Only valid in karaoke */
2129 if( *pi_k_runs > 0 )
2131 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2132 *pi_k_runs, *ppi_k_run_lengths );
2134 SetupKaraoke( p_xml_reader, pi_k_runs,
2135 ppi_k_run_lengths, ppi_k_durations );
2142 case XML_READER_TEXT:
2143 psz_node = xml_ReaderValue( p_xml_reader );
2146 /* Turn any multiple-whitespaces into single spaces */
2147 char *s = strpbrk( psz_node, "\t\r\n " );
2150 int i_whitespace = strspn( s, "\t\r\n " );
2152 if( i_whitespace > 1 )
2155 strlen( s ) - i_whitespace + 1 );
2158 s = strpbrk( s, "\t\r\n " );
2160 SetupLine( p_filter, psz_node, &psz_text,
2161 pi_runs, ppi_run_lengths, ppp_styles,
2162 GetStyleFromFontStack( p_sys,
2171 if( rv != VLC_SUCCESS )
2173 psz_text = psz_text_orig;
2179 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2180 *pi_k_runs, *ppi_k_run_lengths );
2183 *pi_len = psz_text - psz_text_orig;
2185 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2190 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2194 for( k=0; k < p_sys->i_font_attachments; k++ )
2196 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2198 FT_Face p_face = NULL;
2200 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2208 bool match = !strcasecmp( p_face->family_name,
2209 p_style->psz_fontname );
2211 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2212 match = match && p_style->b_bold;
2214 match = match && !p_style->b_bold;
2216 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2217 match = match && p_style->b_italic;
2219 match = match && !p_style->b_italic;
2227 FT_Done_Face( p_face );
2232 return VLC_EGENERIC;
2235 static int ProcessLines( filter_t *p_filter,
2240 uint32_t *pi_run_lengths,
2241 ft_style_t **pp_styles,
2242 line_desc_t **pp_lines,
2244 FT_Vector *p_result,
2248 uint32_t *pi_k_run_lengths,
2249 uint32_t *pi_k_durations )
2251 filter_sys_t *p_sys = p_filter->p_sys;
2252 ft_style_t **pp_char_styles;
2253 int *p_new_positions = NULL;
2254 int8_t *p_levels = NULL;
2255 uint8_t *pi_karaoke_bar = NULL;
2259 /* Assign each character in the text string its style explicitly, so that
2260 * after the characters have been shuffled around by Fribidi, we can re-apply
2261 * the styles, and to simplify the calculation of runs within a line.
2263 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2264 if( !pp_char_styles )
2269 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2270 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2271 * we just won't be able to display the progress bar; at least we'll
2277 for( j = 0; j < i_runs; j++ )
2278 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2279 pp_char_styles[ i++ ] = pp_styles[ j ];
2281 #if defined(HAVE_FRIBIDI)
2283 ft_style_t **pp_char_styles_new;
2284 int *p_old_positions;
2285 uint32_t *p_fribidi_string;
2286 int start_pos, pos = 0;
2288 pp_char_styles_new = (ft_style_t **)
2289 malloc( i_len * sizeof( ft_style_t * ));
2291 p_fribidi_string = (uint32_t *)
2292 malloc( (i_len + 1) * sizeof(uint32_t) );
2293 p_old_positions = (int *)
2294 malloc( (i_len + 1) * sizeof( int ) );
2295 p_new_positions = (int *)
2296 malloc( (i_len + 1) * sizeof( int ) );
2297 p_levels = (int8_t *)
2298 malloc( (i_len + 1) * sizeof( int8_t ) );
2300 if( ! pp_char_styles_new ||
2301 ! p_fribidi_string ||
2302 ! p_old_positions ||
2303 ! p_new_positions ||
2307 free( p_old_positions );
2308 free( p_new_positions );
2309 free( p_fribidi_string );
2310 free( pp_char_styles_new );
2311 free( pi_karaoke_bar );
2313 free( pp_char_styles );
2317 /* Do bidi conversion line-by-line */
2320 while(pos < i_len) {
2321 if (psz_text[pos] != '\n')
2323 p_fribidi_string[pos] = psz_text[pos];
2324 pp_char_styles_new[pos] = pp_char_styles[pos];
2325 p_new_positions[pos] = pos;
2330 while(pos < i_len) {
2331 if (psz_text[pos] == '\n')
2335 if (pos > start_pos)
2337 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2338 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2339 pos - start_pos, &base_dir,
2340 (FriBidiChar*)p_fribidi_string + start_pos,
2341 p_new_positions + start_pos,
2343 p_levels + start_pos );
2344 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2346 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2347 p_old_positions[ j - start_pos ] ];
2348 p_new_positions[ j ] += start_pos;
2352 free( p_old_positions );
2353 free( pp_char_styles );
2354 pp_char_styles = pp_char_styles_new;
2355 psz_text = p_fribidi_string;
2356 p_fribidi_string[ i_len ] = 0;
2359 /* Work out the karaoke */
2360 if( pi_karaoke_bar )
2362 int64_t i_last_duration = 0;
2363 int64_t i_duration = 0;
2364 int64_t i_start_pos = 0;
2365 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2367 for( k = 0; k< i_k_runs; k++ )
2369 double fraction = 0.0;
2371 i_duration += pi_k_durations[ k ];
2373 if( i_duration < i_elapsed )
2375 /* Completely finished this run-length -
2376 * let it render normally */
2380 else if( i_elapsed < i_last_duration )
2382 /* Haven't got up to this segment yet -
2383 * render it completely in karaoke BG mode */
2389 /* Partway through this run */
2391 fraction = (double)(i_elapsed - i_last_duration) /
2392 (double)pi_k_durations[ k ];
2394 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2396 double shade = pi_k_run_lengths[ k ] * fraction;
2398 if( p_new_positions )
2399 j = p_new_positions[ i_start_pos + i ];
2401 j = i_start_pos + i;
2403 if( i < (uint32_t)shade )
2404 pi_karaoke_bar[ j ] = 0xff;
2405 else if( (double)i > shade )
2406 pi_karaoke_bar[ j ] = 0x00;
2409 shade -= (int)shade;
2410 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2411 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2415 i_last_duration = i_duration;
2416 i_start_pos += pi_k_run_lengths[ k ];
2420 free( p_new_positions );
2422 FT_Vector tmp_result;
2424 line_desc_t *p_line = NULL;
2425 line_desc_t *p_prev = NULL;
2431 p_result->x = p_result->y = 0;
2432 tmp_result.x = tmp_result.y = 0;
2435 for( k = 0; k <= (uint32_t) i_len; k++ )
2437 if( ( k == (uint32_t) i_len ) ||
2439 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2441 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2443 /* End of the current style run */
2444 FT_Face p_face = NULL;
2447 /* Look for a match amongst our attachments first */
2448 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2452 char *psz_fontfile = NULL;
2454 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2455 if( p_sys->b_fontconfig_ok )
2457 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2458 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2459 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2460 p_style->psz_fontname,
2465 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2467 if( psz_fontfile && ! *psz_fontfile )
2469 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2470 " so using default font", p_style->psz_fontname,
2471 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2472 (p_style->b_bold ? "(Bold)" :
2473 (p_style->b_italic ? "(Italic)" : ""))) );
2474 free( psz_fontfile );
2475 psz_fontfile = NULL;
2480 if( FT_New_Face( p_sys->p_library,
2481 psz_fontfile, i_idx, &p_face ) )
2483 free( psz_fontfile );
2484 free( pp_char_styles );
2485 #if defined(HAVE_FRIBIDI)
2488 free( pi_karaoke_bar );
2489 return VLC_EGENERIC;
2491 free( psz_fontfile );
2495 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2497 /* We've loaded a font face which is unhelpful for actually
2498 * rendering text - fallback to the default one.
2500 FT_Done_Face( p_face );
2504 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2505 ft_encoding_unicode ) ||
2506 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2507 p_style->i_font_size ) )
2509 if( p_face ) FT_Done_Face( p_face );
2510 free( pp_char_styles );
2511 #if defined(HAVE_FRIBIDI)
2514 free( pi_karaoke_bar );
2515 return VLC_EGENERIC;
2517 p_sys->i_use_kerning =
2518 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2521 uint32_t *psz_unicode = (uint32_t *)
2522 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2525 if( p_face ) FT_Done_Face( p_face );
2526 free( pp_char_styles );
2527 free( psz_unicode );
2528 #if defined(HAVE_FRIBIDI)
2531 free( pi_karaoke_bar );
2534 memcpy( psz_unicode, psz_text + i_prev,
2535 sizeof( uint32_t ) * ( k - i_prev ) );
2536 psz_unicode[ k - i_prev ] = 0;
2537 while( *psz_unicode )
2541 if( !(p_line = NewLine( i_len - i_prev)) )
2543 if( p_face ) FT_Done_Face( p_face );
2544 free( pp_char_styles );
2545 free( psz_unicode );
2546 #if defined(HAVE_FRIBIDI)
2549 free( pi_karaoke_bar );
2552 /* New Color mode only works in YUVA rendering mode --
2553 * (RGB mode has palette constraints on it). We therefore
2554 * need to populate the legacy colour fields also.
2556 p_line->b_new_color_mode = true;
2557 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2558 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2559 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2560 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2561 p_line->p_next = NULL;
2563 i_pen_y += tmp_result.y;
2567 if( p_prev ) p_prev->p_next = p_line;
2568 else *pp_lines = p_line;
2571 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2572 p_style->i_font_color, p_style->b_underline,
2573 p_style->i_karaoke_bg_color,
2574 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2575 &tmp_result ) != VLC_SUCCESS )
2577 if( p_face ) FT_Done_Face( p_face );
2578 free( pp_char_styles );
2579 free( psz_unicode );
2580 #if defined(HAVE_FRIBIDI)
2583 free( pi_karaoke_bar );
2584 return VLC_EGENERIC;
2589 p_result->x = __MAX( p_result->x, tmp_result.x );
2590 p_result->y += tmp_result.y;
2595 if( *psz_unicode == '\n')
2599 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2601 *c_ptr = *(c_ptr+1);
2606 free( psz_unicode );
2607 if( p_face ) FT_Done_Face( p_face );
2611 free( pp_char_styles );
2612 #if defined(HAVE_FRIBIDI)
2617 p_result->x = __MAX( p_result->x, tmp_result.x );
2618 p_result->y += tmp_result.y;
2621 if( pi_karaoke_bar )
2624 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2626 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2628 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2632 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2634 /* 100% BG colour will render faster if we
2635 * instead make it 100% FG colour, so leave
2636 * the ratio alone and copy the value across
2638 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2642 if( pi_karaoke_bar[ i ] & 0x80 )
2644 /* Swap Left and Right sides over for Right aligned
2645 * language text (eg. Arabic, Hebrew)
2647 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2649 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2650 p_line->p_bg_rgb[ k ] = i_tmp;
2652 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2655 /* Jump over the '\n' at the line-end */
2658 free( pi_karaoke_bar );
2664 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2665 subpicture_region_t *p_region_in )
2667 int rv = VLC_SUCCESS;
2668 stream_t *p_sub = NULL;
2669 xml_t *p_xml = NULL;
2670 xml_reader_t *p_xml_reader = NULL;
2672 if( !p_region_in || !p_region_in->psz_html )
2673 return VLC_EGENERIC;
2675 /* Reset the default fontsize in case screen metrics have changed */
2676 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2678 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2679 (uint8_t *) p_region_in->psz_html,
2680 strlen( p_region_in->psz_html ),
2684 p_xml = xml_Create( p_filter );
2687 bool b_karaoke = false;
2689 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2692 /* Look for Root Node */
2693 if( xml_ReaderRead( p_xml_reader ) == 1 )
2695 char *psz_node = xml_ReaderName( p_xml_reader );
2697 if( !strcasecmp( "karaoke", psz_node ) )
2699 /* We're going to have to render the text a number
2700 * of times to show the progress marker on the text.
2702 var_SetBool( p_filter, "text-rerender", true );
2705 else if( !strcasecmp( "text", psz_node ) )
2711 /* Only text and karaoke tags are supported */
2712 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2713 xml_ReaderDelete( p_xml, p_xml_reader );
2714 p_xml_reader = NULL;
2726 uint32_t i_runs = 0;
2727 uint32_t i_k_runs = 0;
2728 uint32_t *pi_run_lengths = NULL;
2729 uint32_t *pi_k_run_lengths = NULL;
2730 uint32_t *pi_k_durations = NULL;
2731 ft_style_t **pp_styles = NULL;
2733 line_desc_t *p_lines = NULL;
2735 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2736 sizeof( uint32_t ) );
2741 rv = ProcessNodes( p_filter, p_xml_reader,
2742 p_region_in->p_style, psz_text, &i_len,
2743 &i_runs, &pi_run_lengths, &pp_styles,
2744 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2747 p_region_out->i_x = p_region_in->i_x;
2748 p_region_out->i_y = p_region_in->i_y;
2750 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2752 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2753 pi_run_lengths, pp_styles, &p_lines, &result,
2754 b_karaoke, i_k_runs, pi_k_run_lengths,
2758 for( k=0; k<i_runs; k++)
2759 DeleteStyle( pp_styles[k] );
2761 free( pi_run_lengths );
2764 /* Don't attempt to render text that couldn't be layed out
2767 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2769 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2771 Render( p_filter, p_region_out, p_lines,
2772 result.x, result.y );
2776 RenderYUVA( p_filter, p_region_out, p_lines,
2777 result.x, result.y );
2781 FreeLines( p_lines );
2783 xml_ReaderDelete( p_xml, p_xml_reader );
2785 xml_Delete( p_xml );
2787 stream_Delete( p_sub );
2793 static char* FontConfig_Select( FcConfig* priv, const char* family,
2794 bool b_bold, bool b_italic, int *i_idx )
2797 FcPattern *pat, *p_pat;
2801 pat = FcPatternCreate();
2802 if (!pat) return NULL;
2804 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2805 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2806 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2807 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2809 FcDefaultSubstitute( pat );
2811 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2813 FcPatternDestroy( pat );
2817 p_pat = FcFontMatch( priv, pat, &result );
2818 FcPatternDestroy( pat );
2819 if( !p_pat ) return NULL;
2821 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2822 || ( val_b != FcTrue ) )
2824 FcPatternDestroy( p_pat );
2827 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2832 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2834 FcPatternDestroy( p_pat );
2839 if( strcasecmp((const char*)val_s, family ) != 0 )
2840 msg_Warn( p_filter, "fontconfig: selected font family is not"
2841 "the requested one: '%s' != '%s'\n",
2842 (const char*)val_s, family );
2845 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2847 FcPatternDestroy( p_pat );
2851 FcPatternDestroy( p_pat );
2852 return strdup( (const char*)val_s );
2856 static void FreeLine( line_desc_t *p_line )
2859 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2861 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2863 free( p_line->pp_glyphs );
2864 free( p_line->p_glyph_pos );
2865 free( p_line->p_fg_rgb );
2866 free( p_line->p_bg_rgb );
2867 free( p_line->p_fg_bg_ratio );
2868 free( p_line->pi_underline_offset );
2869 free( p_line->pi_underline_thickness );
2873 static void FreeLines( line_desc_t *p_lines )
2875 line_desc_t *p_line, *p_next;
2877 if( !p_lines ) return;
2879 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2881 p_next = p_line->p_next;
2886 static line_desc_t *NewLine( int i_count )
2888 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2890 if( !p_line ) return NULL;
2891 p_line->i_height = 0;
2892 p_line->i_width = 0;
2893 p_line->p_next = NULL;
2895 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2896 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2897 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2898 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2899 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2900 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2901 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2902 if( ( p_line->pp_glyphs == NULL ) ||
2903 ( p_line->p_glyph_pos == NULL ) ||
2904 ( p_line->p_fg_rgb == NULL ) ||
2905 ( p_line->p_bg_rgb == NULL ) ||
2906 ( p_line->p_fg_bg_ratio == NULL ) ||
2907 ( p_line->pi_underline_offset == NULL ) ||
2908 ( p_line->pi_underline_thickness == NULL ) )
2910 free( p_line->pi_underline_thickness );
2911 free( p_line->pi_underline_offset );
2912 free( p_line->p_fg_rgb );
2913 free( p_line->p_bg_rgb );
2914 free( p_line->p_fg_bg_ratio );
2915 free( p_line->p_glyph_pos );
2916 free( p_line->pp_glyphs );
2920 p_line->pp_glyphs[0] = NULL;
2921 p_line->b_new_color_mode = false;
2926 static int GetFontSize( filter_t *p_filter )
2928 filter_sys_t *p_sys = p_filter->p_sys;
2932 if( p_sys->i_default_font_size )
2934 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2935 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2937 i_size = p_sys->i_default_font_size;
2941 var_Get( p_filter, "freetype-rel-fontsize", &val );
2942 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2943 p_filter->p_sys->i_display_height =
2944 p_filter->fmt_out.video.i_height;
2948 msg_Warn( p_filter, "invalid fontsize, using 12" );
2949 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2950 i_size = 12 * val.i_int / 1000;
2957 static int SetFontSize( filter_t *p_filter, int i_size )
2959 filter_sys_t *p_sys = p_filter->p_sys;
2963 i_size = GetFontSize( p_filter );
2965 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2968 p_sys->i_font_size = i_size;
2970 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2972 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2973 return VLC_EGENERIC;
2979 static void YUVFromRGB( uint32_t i_argb,
2980 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2982 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2983 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2984 int i_blue = ( i_argb & 0x000000ff );
2986 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2987 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2988 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2989 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2990 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2991 -585 * i_blue + 4096 + 1048576) >> 13, 240);