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 );
528 int canc = vlc_savecancel ();
530 //msg_Dbg( p_this, "Building font database..." );
531 msg_Dbg( p_this, "Building font database..." );
533 if(! FcConfigBuildFonts( p_fontconfig ))
535 /* Don't destroy the fontconfig object - we won't be able to do
536 * italics or bold or change the font face, but we will still
537 * be able to do underline and change the font size.
539 msg_Err( p_this, "fontconfig database can't be built. "
540 "Font styling won't be available" );
544 msg_Dbg( p_this, "Finished building font database." );
545 msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
547 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
548 p_this->p_private = p_fontconfig;
549 vlc_mutex_unlock( p_lock );
551 var_SetBool( p_this, "build-done", true );
552 vlc_restorecancel (canc);
556 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
558 filter_sys_t *p_sys = p_filter->p_sys;
560 p_sys->p_fontconfig = p_fontbuilder->p_private;
561 p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
563 static void FontBuilderDestructor( vlc_object_t *p_this )
565 FcConfig *p_fontconfig = p_this->p_private;
568 FcConfigDestroy( p_fontconfig );
570 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
571 vlc_value_t oldval, vlc_value_t newval, void *param )
573 filter_t *p_filter = param;
577 vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
579 FontBuilderGetFcConfig( p_filter, p_this );
581 vlc_mutex_unlock( p_lock );
590 /*****************************************************************************
591 * Make any TTF/OTF fonts present in the attachments of the media file
592 * and store them for later use by the FreeType Engine
593 *****************************************************************************/
594 static int LoadFontsFromAttachments( filter_t *p_filter )
596 filter_sys_t *p_sys = p_filter->p_sys;
597 input_thread_t *p_input;
598 input_attachment_t **pp_attachments;
599 int i_attachments_cnt;
601 int rv = VLC_SUCCESS;
603 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
607 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
609 vlc_object_release(p_input);
613 p_sys->i_font_attachments = 0;
614 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
615 if(! p_sys->pp_font_attachments )
618 for( k = 0; k < i_attachments_cnt; k++ )
620 input_attachment_t *p_attach = pp_attachments[k];
622 if( p_sys->pp_font_attachments )
624 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
625 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
626 ( p_attach->i_data > 0 ) &&
627 ( p_attach->p_data != NULL ) )
629 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
633 vlc_input_attachment_Delete( p_attach );
638 vlc_input_attachment_Delete( p_attach );
641 free( pp_attachments );
643 vlc_object_release(p_input);
648 /*****************************************************************************
649 * Render: place string in picture
650 *****************************************************************************
651 * This function merges the previously rendered freetype glyphs into a picture
652 *****************************************************************************/
653 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
654 line_desc_t *p_line, int i_width, int i_height )
656 static const uint8_t pi_gamma[16] =
657 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
658 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
662 int i, x, y, i_pitch;
663 uint8_t i_y; /* YUV values, derived from incoming RGB */
665 subpicture_region_t *p_region_tmp;
667 /* Create a new subpicture region */
668 memset( &fmt, 0, sizeof(video_format_t) );
669 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
671 fmt.i_width = fmt.i_visible_width = i_width + 4;
672 fmt.i_height = fmt.i_visible_height = i_height + 4;
673 if( p_region->fmt.i_visible_width > 0 )
674 fmt.i_visible_width = p_region->fmt.i_visible_width;
675 if( p_region->fmt.i_visible_height > 0 )
676 fmt.i_visible_height = p_region->fmt.i_visible_height;
677 fmt.i_x_offset = fmt.i_y_offset = 0;
678 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
681 msg_Err( p_filter, "cannot allocate SPU region" );
685 p_region->fmt = p_region_tmp->fmt;
686 p_region->picture = p_region_tmp->picture;
687 free( p_region_tmp );
689 /* Calculate text color components */
690 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
691 25 * p_line->i_blue + 128) >> 8) + 16;
692 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
693 112 * p_line->i_blue + 128) >> 8) + 128;
694 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
695 18 * p_line->i_blue + 128) >> 8) + 128;
698 fmt.p_palette->i_entries = 16;
699 for( i = 0; i < 8; i++ )
701 fmt.p_palette->palette[i][0] = 0;
702 fmt.p_palette->palette[i][1] = 0x80;
703 fmt.p_palette->palette[i][2] = 0x80;
704 fmt.p_palette->palette[i][3] = pi_gamma[i];
705 fmt.p_palette->palette[i][3] =
706 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
708 for( i = 8; i < fmt.p_palette->i_entries; i++ )
710 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
711 fmt.p_palette->palette[i][1] = i_u;
712 fmt.p_palette->palette[i][2] = i_v;
713 fmt.p_palette->palette[i][3] = pi_gamma[i];
714 fmt.p_palette->palette[i][3] =
715 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
718 p_dst = p_region->picture.Y_PIXELS;
719 i_pitch = p_region->picture.Y_PITCH;
721 /* Initialize the region pixels */
722 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
724 for( ; p_line != NULL; p_line = p_line->p_next )
726 int i_glyph_tmax = 0;
727 int i_bitmap_offset, i_offset, i_align_offset = 0;
728 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
730 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
731 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
734 if( p_line->i_width < i_width )
736 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
738 i_align_offset = i_width - p_line->i_width;
740 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
742 i_align_offset = ( i_width - p_line->i_width ) / 2;
746 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
748 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
750 i_offset = ( p_line->p_glyph_pos[ i ].y +
751 i_glyph_tmax - p_glyph->top + 2 ) *
752 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
755 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
757 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
759 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
761 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
768 /* Outlining (find something better than nearest neighbour filtering ?) */
771 uint8_t *p_dst = p_region->picture.Y_PIXELS;
772 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
773 uint8_t left, current;
775 for( y = 1; y < (int)fmt.i_height - 1; y++ )
777 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
778 p_dst += p_region->picture.Y_PITCH;
781 for( x = 1; x < (int)fmt.i_width - 1; x++ )
784 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
785 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;
789 memset( p_top, 0, fmt.i_width );
795 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
796 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
797 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
798 int i_glyph_tmax, int i_align_offset,
799 uint8_t i_y, uint8_t i_u, uint8_t i_v,
800 subpicture_region_t *p_region)
804 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
806 p_dst_y = p_region->picture.Y_PIXELS;
807 p_dst_u = p_region->picture.U_PIXELS;
808 p_dst_v = p_region->picture.V_PIXELS;
809 p_dst_a = p_region->picture.A_PIXELS;
810 i_pitch = p_region->picture.A_PITCH;
812 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
813 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
815 for( y = 0; y < i_line_thickness; y++ )
817 int i_extra = p_this_glyph->bitmap.width;
821 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
822 (p_this_glyph_pos->x + p_this_glyph->left);
824 for( x = 0; x < i_extra; x++ )
828 /* break the underline around the tails of any glyphs which cross it */
829 for( z = x - i_line_thickness;
830 z < x + i_line_thickness && b_ok;
833 if( p_next_glyph && ( z >= i_extra ) )
835 int i_row = i_line_offset + p_next_glyph->top + y;
837 if( ( p_next_glyph->bitmap.rows > i_row ) &&
838 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
843 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
845 int i_row = i_line_offset + p_this_glyph->top + y;
847 if( ( p_this_glyph->bitmap.rows > i_row ) &&
848 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
857 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
858 p_dst_u[i_offset+x] = i_u;
859 p_dst_v[i_offset+x] = i_v;
860 p_dst_a[i_offset+x] = 255;
867 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
869 uint8_t *p_dst = p_region->picture.A_PIXELS;
870 int i_pitch = p_region->picture.A_PITCH;
873 for( ; p_line != NULL; p_line = p_line->p_next )
875 int i_glyph_tmax=0, i = 0;
876 int i_bitmap_offset, i_offset, i_align_offset = 0;
877 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
879 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
880 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
883 if( p_line->i_width < i_width )
885 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
887 i_align_offset = i_width - p_line->i_width;
889 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
891 i_align_offset = ( i_width - p_line->i_width ) / 2;
895 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
897 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
899 i_offset = ( p_line->p_glyph_pos[ i ].y +
900 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
901 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
902 i_align_offset +xoffset;
904 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
906 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
908 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
909 if( p_dst[i_offset+x] <
910 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
912 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
921 /*****************************************************************************
922 * Render: place string in picture
923 *****************************************************************************
924 * This function merges the previously rendered freetype glyphs into a picture
925 *****************************************************************************/
926 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
927 line_desc_t *p_line, int i_width, int i_height )
929 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
931 int i, x, y, i_pitch, i_alpha;
932 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
933 subpicture_region_t *p_region_tmp;
935 if( i_width == 0 || i_height == 0 )
938 /* Create a new subpicture region */
939 memset( &fmt, 0, sizeof(video_format_t) );
940 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
942 fmt.i_width = fmt.i_visible_width = i_width + 6;
943 fmt.i_height = fmt.i_visible_height = i_height + 6;
944 if( p_region->fmt.i_visible_width > 0 )
945 fmt.i_visible_width = p_region->fmt.i_visible_width;
946 if( p_region->fmt.i_visible_height > 0 )
947 fmt.i_visible_height = p_region->fmt.i_visible_height;
948 fmt.i_x_offset = fmt.i_y_offset = 0;
949 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
952 msg_Err( p_filter, "cannot allocate SPU region" );
956 p_region->fmt = p_region_tmp->fmt;
957 p_region->picture = p_region_tmp->picture;
958 free( p_region_tmp );
960 /* Calculate text color components */
961 YUVFromRGB( (p_line->i_red << 16) |
962 (p_line->i_green << 8) |
965 i_alpha = p_line->i_alpha;
967 p_dst_y = p_region->picture.Y_PIXELS;
968 p_dst_u = p_region->picture.U_PIXELS;
969 p_dst_v = p_region->picture.V_PIXELS;
970 p_dst_a = p_region->picture.A_PIXELS;
971 i_pitch = p_region->picture.A_PITCH;
973 /* Initialize the region pixels */
974 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
976 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
977 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
978 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
979 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
983 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
984 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
985 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
986 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
988 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
989 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
991 DrawBlack( p_line, i_width, p_region, 0, 0);
992 DrawBlack( p_line, i_width, p_region, -1, 0);
993 DrawBlack( p_line, i_width, p_region, 0, -1);
994 DrawBlack( p_line, i_width, p_region, 1, 0);
995 DrawBlack( p_line, i_width, p_region, 0, 1);
998 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
1000 DrawBlack( p_line, i_width, p_region, -1, -1);
1001 DrawBlack( p_line, i_width, p_region, -1, 1);
1002 DrawBlack( p_line, i_width, p_region, 1, -1);
1003 DrawBlack( p_line, i_width, p_region, 1, 1);
1005 DrawBlack( p_line, i_width, p_region, -2, 0);
1006 DrawBlack( p_line, i_width, p_region, 0, -2);
1007 DrawBlack( p_line, i_width, p_region, 2, 0);
1008 DrawBlack( p_line, i_width, p_region, 0, 2);
1010 DrawBlack( p_line, i_width, p_region, -2, -2);
1011 DrawBlack( p_line, i_width, p_region, -2, 2);
1012 DrawBlack( p_line, i_width, p_region, 2, -2);
1013 DrawBlack( p_line, i_width, p_region, 2, 2);
1015 DrawBlack( p_line, i_width, p_region, -3, 0);
1016 DrawBlack( p_line, i_width, p_region, 0, -3);
1017 DrawBlack( p_line, i_width, p_region, 3, 0);
1018 DrawBlack( p_line, i_width, p_region, 0, 3);
1021 for( ; p_line != NULL; p_line = p_line->p_next )
1023 int i_glyph_tmax = 0;
1024 int i_bitmap_offset, i_offset, i_align_offset = 0;
1025 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1027 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1028 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1031 if( p_line->i_width < i_width )
1033 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1035 i_align_offset = i_width - p_line->i_width;
1037 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1039 i_align_offset = ( i_width - p_line->i_width ) / 2;
1043 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1045 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1047 i_offset = ( p_line->p_glyph_pos[ i ].y +
1048 i_glyph_tmax - p_glyph->top + 3 ) *
1049 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1052 if( p_line->b_new_color_mode )
1054 /* Every glyph can (and in fact must) have its own color */
1055 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1058 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1060 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1062 uint8_t i_y_local = i_y;
1063 uint8_t i_u_local = i_u;
1064 uint8_t i_v_local = i_v;
1066 if( p_line->p_fg_bg_ratio != 0x00 )
1068 int i_split = p_glyph->bitmap.width *
1069 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1073 YUVFromRGB( p_line->p_bg_rgb[ i ],
1074 &i_y_local, &i_u_local, &i_v_local );
1078 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1080 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1081 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1083 p_dst_u[i_offset+x] = i_u;
1084 p_dst_v[i_offset+x] = i_v;
1086 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1087 p_dst_a[i_offset+x] = 0xff;
1090 i_offset += i_pitch;
1093 if( p_line->pi_underline_thickness[ i ] )
1095 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1096 p_line->pi_underline_offset[ i ],
1097 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1098 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1099 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1100 i_glyph_tmax, i_align_offset,
1107 /* Apply the alpha setting */
1108 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1109 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1115 * This function renders a text subpicture region into another one.
1116 * It also calculates the size needed for this string, and renders the
1117 * needed glyphs into memory. It is used as pf_add_string callback in
1118 * the vout method by this module
1120 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1121 subpicture_region_t *p_region_in )
1123 filter_sys_t *p_sys = p_filter->p_sys;
1124 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1125 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1126 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1127 int i_string_length;
1129 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1130 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1140 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1141 psz_string = p_region_in->psz_text;
1142 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1144 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1145 i_scale = val.i_int;
1147 if( p_region_in->p_style )
1149 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1150 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1151 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1155 i_font_color = p_sys->i_font_color;
1156 i_font_alpha = 255 - p_sys->i_font_opacity;
1157 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1160 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1161 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1162 SetFontSize( p_filter, i_font_size );
1164 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1165 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1166 i_blue = i_font_color & 0x000000FF;
1168 result.x = result.y = 0;
1169 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1171 psz_unicode = psz_unicode_orig =
1172 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1173 if( psz_unicode == NULL )
1175 #if defined(WORDS_BIGENDIAN)
1176 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1178 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1180 if( iconv_handle == (vlc_iconv_t)-1 )
1182 msg_Warn( p_filter, "unable to do conversion" );
1188 const char *p_in_buffer = psz_string;
1189 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1190 i_in_bytes = strlen( psz_string );
1191 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1192 i_out_bytes_left = i_out_bytes;
1193 p_out_buffer = (char *)psz_unicode;
1194 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1196 &p_out_buffer, &i_out_bytes_left );
1198 vlc_iconv_close( iconv_handle );
1202 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1203 "bytes left %u", (unsigned)i_in_bytes );
1206 *(uint32_t*)p_out_buffer = 0;
1207 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1210 #if defined(HAVE_FRIBIDI)
1212 uint32_t *p_fribidi_string;
1213 int32_t start_pos, pos = 0;
1215 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1216 if( !p_fribidi_string )
1219 /* Do bidi conversion line-by-line */
1220 while( pos < i_string_length )
1222 while( pos < i_string_length )
1224 i_char = psz_unicode[pos];
1225 if (i_char != '\r' && i_char != '\n')
1227 p_fribidi_string[pos] = i_char;
1231 while( pos < i_string_length )
1233 i_char = psz_unicode[pos];
1234 if (i_char == '\r' || i_char == '\n')
1238 if (pos > start_pos)
1240 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1241 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1244 (FriBidiChar*)p_fribidi_string + start_pos,
1249 free( psz_unicode_orig );
1250 psz_unicode = psz_unicode_orig = p_fribidi_string;
1251 p_fribidi_string[ i_string_length ] = 0;
1255 /* Calculate relative glyph positions and a bounding box for the
1257 if( !(p_line = NewLine( strlen( psz_string ))) )
1260 i_pen_x = i_pen_y = 0;
1262 psz_line_start = psz_unicode;
1264 #define face p_sys->p_face
1265 #define glyph face->glyph
1267 while( *psz_unicode )
1269 i_char = *psz_unicode++;
1270 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1275 if( i_char == '\n' )
1277 psz_line_start = psz_unicode;
1278 if( !(p_next = NewLine( strlen( psz_string ))) )
1280 p_line->p_next = p_next;
1281 p_line->i_width = line.xMax;
1282 p_line->i_height = face->size->metrics.height >> 6;
1283 p_line->pp_glyphs[ i ] = NULL;
1284 p_line->i_alpha = i_font_alpha;
1285 p_line->i_red = i_red;
1286 p_line->i_green = i_green;
1287 p_line->i_blue = i_blue;
1290 result.x = __MAX( result.x, line.xMax );
1291 result.y += face->size->metrics.height >> 6;
1294 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1295 i_pen_y += face->size->metrics.height >> 6;
1297 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1302 i_glyph_index = FT_Get_Char_Index( face, i_char );
1303 if( p_sys->i_use_kerning && i_glyph_index
1307 FT_Get_Kerning( face, i_previous, i_glyph_index,
1308 ft_kerning_default, &delta );
1309 i_pen_x += delta.x >> 6;
1312 p_line->p_glyph_pos[ i ].x = i_pen_x;
1313 p_line->p_glyph_pos[ i ].y = i_pen_y;
1314 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1317 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1321 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1324 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1328 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1329 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1332 FT_Done_Glyph( tmp_glyph );
1335 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1338 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1339 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1340 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1342 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1343 p_line->pp_glyphs[ i ] = NULL;
1345 p_line = NewLine( strlen( psz_string ));
1346 if( p_prev ) p_prev->p_next = p_line;
1347 else p_lines = p_line;
1349 uint32_t *psz_unicode_saved = psz_unicode;
1350 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1354 if( psz_unicode == psz_line_start )
1355 { /* try harder to break that line */
1356 psz_unicode = psz_unicode_saved;
1357 while( psz_unicode > psz_line_start &&
1358 *psz_unicode != '_' && *psz_unicode != '/' &&
1359 *psz_unicode != '\\' && *psz_unicode != '.' )
1364 if( psz_unicode == psz_line_start )
1366 msg_Warn( p_filter, "unbreakable string" );
1371 *psz_unicode = '\n';
1373 psz_unicode = psz_line_start;
1376 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1379 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1380 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1382 i_previous = i_glyph_index;
1383 i_pen_x += glyph->advance.x >> 6;
1387 p_line->i_width = line.xMax;
1388 p_line->i_height = face->size->metrics.height >> 6;
1389 p_line->pp_glyphs[ i ] = NULL;
1390 p_line->i_alpha = i_font_alpha;
1391 p_line->i_red = i_red;
1392 p_line->i_green = i_green;
1393 p_line->i_blue = i_blue;
1394 result.x = __MAX( result.x, line.xMax );
1395 result.y += line.yMax - line.yMin;
1400 p_region_out->i_x = p_region_in->i_x;
1401 p_region_out->i_y = p_region_in->i_y;
1403 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1404 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1406 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1408 free( psz_unicode_orig );
1409 FreeLines( p_lines );
1413 free( psz_unicode_orig );
1414 FreeLines( p_lines );
1415 return VLC_EGENERIC;
1418 #ifdef HAVE_FONTCONFIG
1419 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1420 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1421 bool b_italic, bool b_uline )
1423 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1427 p_style->i_font_size = i_font_size;
1428 p_style->i_font_color = i_font_color;
1429 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1430 p_style->b_italic = b_italic;
1431 p_style->b_bold = b_bold;
1432 p_style->b_underline = b_uline;
1434 p_style->psz_fontname = strdup( psz_fontname );
1439 static void DeleteStyle( ft_style_t *p_style )
1443 free( p_style->psz_fontname );
1448 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1455 if(( s1->i_font_size == s2->i_font_size ) &&
1456 ( s1->i_font_color == s2->i_font_color ) &&
1457 ( s1->b_italic == s2->b_italic ) &&
1458 ( s1->b_bold == s2->b_bold ) &&
1459 ( s1->b_underline == s2->b_underline ) &&
1460 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1467 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1468 uint32_t i_color, uint32_t i_karaoke_bg_color )
1470 font_stack_t *p_new;
1473 return VLC_EGENERIC;
1475 p_new = malloc( sizeof( font_stack_t ) );
1479 p_new->p_next = NULL;
1482 p_new->psz_name = strdup( psz_name );
1484 p_new->psz_name = NULL;
1486 p_new->i_size = i_size;
1487 p_new->i_color = i_color;
1488 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1496 font_stack_t *p_last;
1498 for( p_last = *p_font;
1500 p_last = p_last->p_next )
1503 p_last->p_next = p_new;
1508 static int PopFont( font_stack_t **p_font )
1510 font_stack_t *p_last, *p_next_to_last;
1512 if( !p_font || !*p_font )
1513 return VLC_EGENERIC;
1515 p_next_to_last = NULL;
1516 for( p_last = *p_font;
1518 p_last = p_last->p_next )
1520 p_next_to_last = p_last;
1523 if( p_next_to_last )
1524 p_next_to_last->p_next = NULL;
1528 free( p_last->psz_name );
1534 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1535 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1537 font_stack_t *p_last;
1539 if( !p_font || !*p_font )
1540 return VLC_EGENERIC;
1542 for( p_last=*p_font;
1544 p_last=p_last->p_next )
1547 *psz_name = p_last->psz_name;
1548 *i_size = p_last->i_size;
1549 *i_color = p_last->i_color;
1550 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1555 static void IconvText( filter_t *p_filter, const char *psz_string,
1556 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1558 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1560 /* If memory hasn't been allocated for our output string, allocate it here
1561 * - the calling function must now be responsible for freeing it.
1563 if( !*ppsz_unicode )
1564 *ppsz_unicode = (uint32_t *)
1565 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1567 /* We don't need to handle a NULL pointer in *ppsz_unicode
1568 * if we are instead testing for a non NULL value like we are here */
1572 #if defined(WORDS_BIGENDIAN)
1573 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1575 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1577 if( iconv_handle != (vlc_iconv_t)-1 )
1579 char *p_in_buffer, *p_out_buffer;
1580 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1581 i_in_bytes = strlen( psz_string );
1582 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1583 i_out_bytes_left = i_out_bytes;
1584 p_in_buffer = (char *) psz_string;
1585 p_out_buffer = (char *) *ppsz_unicode;
1586 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1587 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1589 vlc_iconv_close( iconv_handle );
1593 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1594 "bytes left %u", (unsigned)i_in_bytes );
1598 *(uint32_t*)p_out_buffer = 0;
1600 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1605 msg_Warn( p_filter, "unable to do conversion" );
1610 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1611 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1614 ft_style_t *p_style = NULL;
1616 char *psz_fontname = NULL;
1617 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1618 uint32_t i_karaoke_bg_color = i_font_color;
1619 int i_font_size = p_sys->i_font_size;
1621 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1622 &i_font_color, &i_karaoke_bg_color ))
1624 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1625 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1630 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1631 bool b_uline, int i_karaoke_bgcolor,
1632 line_desc_t *p_line, uint32_t *psz_unicode,
1633 int *pi_pen_x, int i_pen_y, int *pi_start,
1634 FT_Vector *p_result )
1639 bool b_first_on_line = true;
1642 int i_pen_x_start = *pi_pen_x;
1644 uint32_t *psz_unicode_start = psz_unicode;
1646 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1648 /* Account for part of line already in position */
1649 for( i=0; i<*pi_start; i++ )
1653 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1654 ft_glyph_bbox_pixels, &glyph_size );
1656 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1657 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1658 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1659 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1665 b_first_on_line = false;
1667 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1673 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1674 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1678 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1679 ft_kerning_default, &delta );
1680 *pi_pen_x += delta.x >> 6;
1682 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1683 p_line->p_glyph_pos[ i ].y = i_pen_y;
1685 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1689 "unable to render text FT_Load_Glyph returned %d", i_error );
1690 p_line->pp_glyphs[ i ] = NULL;
1691 return VLC_EGENERIC;
1693 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1697 "unable to render text FT_Get_Glyph returned %d", i_error );
1698 p_line->pp_glyphs[ i ] = NULL;
1699 return VLC_EGENERIC;
1701 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1702 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1705 FT_Done_Glyph( tmp_glyph );
1710 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1711 p_face->size->metrics.y_scale));
1712 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1713 p_face->size->metrics.y_scale));
1715 p_line->pi_underline_offset[ i ] =
1716 ( aOffset < 0 ) ? -aOffset : aOffset;
1717 p_line->pi_underline_thickness[ i ] =
1718 ( aSize < 0 ) ? -aSize : aSize;
1720 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1721 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1722 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1723 p_line->p_fg_bg_ratio[ i ] = 0x00;
1725 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1726 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1727 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1729 while( --i > *pi_start )
1731 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1734 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1738 if( psz_unicode == psz_unicode_start )
1740 if( b_first_on_line )
1742 msg_Warn( p_filter, "unbreakable string" );
1743 p_line->pp_glyphs[ i ] = NULL;
1744 return VLC_EGENERIC;
1746 *pi_pen_x = i_pen_x_start;
1748 p_line->i_width = line.xMax;
1749 p_line->i_height = __MAX( p_line->i_height,
1750 p_face->size->metrics.height >> 6 );
1751 p_line->pp_glyphs[ i ] = NULL;
1753 p_result->x = __MAX( p_result->x, line.xMax );
1754 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1755 i_yMax - i_yMin ) );
1762 *psz_unicode = '\n';
1764 psz_unicode = psz_unicode_start;
1765 *pi_pen_x = i_pen_x_start;
1773 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1774 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1776 i_previous = i_glyph_index;
1777 *pi_pen_x += p_face->glyph->advance.x >> 6;
1780 p_line->i_width = line.xMax;
1781 p_line->i_height = __MAX( p_line->i_height,
1782 p_face->size->metrics.height >> 6 );
1783 p_line->pp_glyphs[ i ] = NULL;
1785 p_result->x = __MAX( p_result->x, line.xMax );
1786 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1787 line.yMax - line.yMin ) );
1791 /* Get rid of any text processed - if necessary repositioning
1792 * at the start of a new line of text
1796 *psz_unicode_start = '\0';
1798 else if( psz_unicode > psz_unicode_start )
1800 for( i=0; psz_unicode[ i ]; i++ )
1801 psz_unicode_start[ i ] = psz_unicode[ i ];
1802 psz_unicode_start[ i ] = '\0';
1808 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1809 font_stack_t **p_fonts, int i_scale )
1812 char *psz_fontname = NULL;
1813 uint32_t i_font_color = 0xffffff;
1814 int i_font_alpha = 0;
1815 uint32_t i_karaoke_bg_color = 0x00ffffff;
1816 int i_font_size = 24;
1818 /* Default all attributes to the top font in the stack -- in case not
1819 * all attributes are specified in the sub-font
1821 if( VLC_SUCCESS == PeekFont( p_fonts,
1825 &i_karaoke_bg_color ))
1827 psz_fontname = strdup( psz_fontname );
1828 i_font_size = i_font_size * 1000 / i_scale;
1830 i_font_alpha = (i_font_color >> 24) & 0xff;
1831 i_font_color &= 0x00ffffff;
1833 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1835 char *psz_name = xml_ReaderName( p_xml_reader );
1836 char *psz_value = xml_ReaderValue( p_xml_reader );
1838 if( psz_name && psz_value )
1840 if( !strcasecmp( "face", psz_name ) )
1842 free( psz_fontname );
1843 psz_fontname = strdup( psz_value );
1845 else if( !strcasecmp( "size", psz_name ) )
1847 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1849 int i_value = atoi( psz_value );
1851 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1852 i_font_size += ( i_value * i_font_size ) / 10;
1853 else if( i_value < -5 )
1854 i_font_size = - i_value;
1855 else if( i_value > 5 )
1856 i_font_size = i_value;
1859 i_font_size = atoi( psz_value );
1861 else if( !strcasecmp( "color", psz_name ) &&
1862 ( psz_value[0] == '#' ) )
1864 i_font_color = strtol( psz_value + 1, NULL, 16 );
1865 i_font_color &= 0x00ffffff;
1867 else if( !strcasecmp( "alpha", psz_name ) &&
1868 ( psz_value[0] == '#' ) )
1870 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1871 i_font_alpha &= 0xff;
1877 rv = PushFont( p_fonts,
1879 i_font_size * i_scale / 1000,
1880 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1881 i_karaoke_bg_color );
1883 free( psz_fontname );
1888 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1889 uint32_t **psz_text_out, uint32_t *pi_runs,
1890 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1891 ft_style_t *p_style )
1893 uint32_t i_string_length = 0;
1895 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1896 *psz_text_out += i_string_length;
1898 if( ppp_styles && ppi_run_lengths )
1904 *ppp_styles = (ft_style_t **)
1905 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1907 else if( *pi_runs == 1 )
1909 *ppp_styles = (ft_style_t **)
1910 malloc( *pi_runs * sizeof( ft_style_t * ) );
1913 /* We have just malloc'ed this memory successfully -
1914 * *pi_runs HAS to be within the memory area of *ppp_styles */
1917 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1921 if( *ppi_run_lengths )
1923 *ppi_run_lengths = (uint32_t *)
1924 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1926 else if( *pi_runs == 1 )
1928 *ppi_run_lengths = (uint32_t *)
1929 malloc( *pi_runs * sizeof( uint32_t ) );
1932 /* same remarks here */
1933 if( *ppi_run_lengths )
1935 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1938 /* If we couldn't use the p_style argument due to memory allocation
1939 * problems above, release it here.
1941 if( p_style ) DeleteStyle( p_style );
1944 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1945 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1947 /* Karaoke tags _PRECEDE_ the text they specify a duration
1948 * for, therefore we are working out the length for the
1949 * previous tag, and first time through we have nothing
1951 if( pi_k_run_lengths )
1956 /* Work out how many characters are presently in the string
1958 for( i = 0; i < i_runs; i++ )
1959 i_chars += pi_run_lengths[ i ];
1961 /* Subtract away those we've already allocated to other
1964 for( i = 0; i < i_k_runs; i++ )
1965 i_chars -= pi_k_run_lengths[ i ];
1967 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1971 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1972 uint32_t **ppi_k_run_lengths,
1973 uint32_t **ppi_k_durations )
1975 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1977 char *psz_name = xml_ReaderName( p_xml_reader );
1978 char *psz_value = xml_ReaderValue( p_xml_reader );
1980 if( psz_name && psz_value &&
1981 !strcasecmp( "t", psz_name ) )
1983 if( ppi_k_durations && ppi_k_run_lengths )
1987 if( *ppi_k_durations )
1989 *ppi_k_durations = (uint32_t *)
1990 realloc( *ppi_k_durations,
1991 *pi_k_runs * sizeof( uint32_t ) );
1993 else if( *pi_k_runs == 1 )
1995 *ppi_k_durations = (uint32_t *)
1996 malloc( *pi_k_runs * sizeof( uint32_t ) );
1999 if( *ppi_k_run_lengths )
2001 *ppi_k_run_lengths = (uint32_t *)
2002 realloc( *ppi_k_run_lengths,
2003 *pi_k_runs * sizeof( uint32_t ) );
2005 else if( *pi_k_runs == 1 )
2007 *ppi_k_run_lengths = (uint32_t *)
2008 malloc( *pi_k_runs * sizeof( uint32_t ) );
2010 if( *ppi_k_durations )
2011 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
2013 if( *ppi_k_run_lengths )
2014 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
2022 static int ProcessNodes( filter_t *p_filter,
2023 xml_reader_t *p_xml_reader,
2024 text_style_t *p_font_style,
2029 uint32_t **ppi_run_lengths,
2030 ft_style_t ***ppp_styles,
2033 uint32_t *pi_k_runs,
2034 uint32_t **ppi_k_run_lengths,
2035 uint32_t **ppi_k_durations )
2037 int rv = VLC_SUCCESS;
2038 filter_sys_t *p_sys = p_filter->p_sys;
2039 uint32_t *psz_text_orig = psz_text;
2040 font_stack_t *p_fonts = NULL;
2044 char *psz_node = NULL;
2046 bool b_italic = false;
2047 bool b_bold = false;
2048 bool b_uline = false;
2050 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2051 i_scale = val.i_int;
2055 rv = PushFont( &p_fonts,
2056 p_font_style->psz_fontname,
2057 p_font_style->i_font_size * i_scale / 1000,
2058 (p_font_style->i_font_color & 0xffffff) |
2059 ((p_font_style->i_font_alpha & 0xff) << 24),
2060 (p_font_style->i_karaoke_background_color & 0xffffff) |
2061 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2063 if( p_font_style->i_style_flags & STYLE_BOLD )
2065 if( p_font_style->i_style_flags & STYLE_ITALIC )
2067 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2072 rv = PushFont( &p_fonts,
2078 if( rv != VLC_SUCCESS )
2081 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2083 switch ( xml_ReaderNodeType( p_xml_reader ) )
2085 case XML_READER_NONE:
2087 case XML_READER_ENDELEM:
2088 psz_node = xml_ReaderName( p_xml_reader );
2092 if( !strcasecmp( "font", psz_node ) )
2093 PopFont( &p_fonts );
2094 else if( !strcasecmp( "b", psz_node ) )
2096 else if( !strcasecmp( "i", psz_node ) )
2098 else if( !strcasecmp( "u", psz_node ) )
2104 case XML_READER_STARTELEM:
2105 psz_node = xml_ReaderName( p_xml_reader );
2108 if( !strcasecmp( "font", psz_node ) )
2109 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2110 else if( !strcasecmp( "b", psz_node ) )
2112 else if( !strcasecmp( "i", psz_node ) )
2114 else if( !strcasecmp( "u", psz_node ) )
2116 else if( !strcasecmp( "br", psz_node ) )
2118 SetupLine( p_filter, "\n", &psz_text,
2119 pi_runs, ppi_run_lengths, ppp_styles,
2120 GetStyleFromFontStack( p_sys,
2126 else if( !strcasecmp( "k", psz_node ) )
2128 /* Only valid in karaoke */
2131 if( *pi_k_runs > 0 )
2133 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2134 *pi_k_runs, *ppi_k_run_lengths );
2136 SetupKaraoke( p_xml_reader, pi_k_runs,
2137 ppi_k_run_lengths, ppi_k_durations );
2144 case XML_READER_TEXT:
2145 psz_node = xml_ReaderValue( p_xml_reader );
2148 /* Turn any multiple-whitespaces into single spaces */
2149 char *s = strpbrk( psz_node, "\t\r\n " );
2152 int i_whitespace = strspn( s, "\t\r\n " );
2154 if( i_whitespace > 1 )
2157 strlen( s ) - i_whitespace + 1 );
2160 s = strpbrk( s, "\t\r\n " );
2162 SetupLine( p_filter, psz_node, &psz_text,
2163 pi_runs, ppi_run_lengths, ppp_styles,
2164 GetStyleFromFontStack( p_sys,
2173 if( rv != VLC_SUCCESS )
2175 psz_text = psz_text_orig;
2181 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2182 *pi_k_runs, *ppi_k_run_lengths );
2185 *pi_len = psz_text - psz_text_orig;
2187 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2192 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2196 for( k=0; k < p_sys->i_font_attachments; k++ )
2198 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2200 FT_Face p_face = NULL;
2202 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2210 bool match = !strcasecmp( p_face->family_name,
2211 p_style->psz_fontname );
2213 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2214 match = match && p_style->b_bold;
2216 match = match && !p_style->b_bold;
2218 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2219 match = match && p_style->b_italic;
2221 match = match && !p_style->b_italic;
2229 FT_Done_Face( p_face );
2234 return VLC_EGENERIC;
2237 static int ProcessLines( filter_t *p_filter,
2242 uint32_t *pi_run_lengths,
2243 ft_style_t **pp_styles,
2244 line_desc_t **pp_lines,
2246 FT_Vector *p_result,
2250 uint32_t *pi_k_run_lengths,
2251 uint32_t *pi_k_durations )
2253 filter_sys_t *p_sys = p_filter->p_sys;
2254 ft_style_t **pp_char_styles;
2255 int *p_new_positions = NULL;
2256 int8_t *p_levels = NULL;
2257 uint8_t *pi_karaoke_bar = NULL;
2261 /* Assign each character in the text string its style explicitly, so that
2262 * after the characters have been shuffled around by Fribidi, we can re-apply
2263 * the styles, and to simplify the calculation of runs within a line.
2265 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2266 if( !pp_char_styles )
2271 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2272 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2273 * we just won't be able to display the progress bar; at least we'll
2279 for( j = 0; j < i_runs; j++ )
2280 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2281 pp_char_styles[ i++ ] = pp_styles[ j ];
2283 #if defined(HAVE_FRIBIDI)
2285 ft_style_t **pp_char_styles_new;
2286 int *p_old_positions;
2287 uint32_t *p_fribidi_string;
2288 int start_pos, pos = 0;
2290 pp_char_styles_new = (ft_style_t **)
2291 malloc( i_len * sizeof( ft_style_t * ));
2293 p_fribidi_string = (uint32_t *)
2294 malloc( (i_len + 1) * sizeof(uint32_t) );
2295 p_old_positions = (int *)
2296 malloc( (i_len + 1) * sizeof( int ) );
2297 p_new_positions = (int *)
2298 malloc( (i_len + 1) * sizeof( int ) );
2299 p_levels = (int8_t *)
2300 malloc( (i_len + 1) * sizeof( int8_t ) );
2302 if( ! pp_char_styles_new ||
2303 ! p_fribidi_string ||
2304 ! p_old_positions ||
2305 ! p_new_positions ||
2309 free( p_old_positions );
2310 free( p_new_positions );
2311 free( p_fribidi_string );
2312 free( pp_char_styles_new );
2313 free( pi_karaoke_bar );
2315 free( pp_char_styles );
2319 /* Do bidi conversion line-by-line */
2322 while(pos < i_len) {
2323 if (psz_text[pos] != '\n')
2325 p_fribidi_string[pos] = psz_text[pos];
2326 pp_char_styles_new[pos] = pp_char_styles[pos];
2327 p_new_positions[pos] = pos;
2332 while(pos < i_len) {
2333 if (psz_text[pos] == '\n')
2337 if (pos > start_pos)
2339 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2340 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2341 pos - start_pos, &base_dir,
2342 (FriBidiChar*)p_fribidi_string + start_pos,
2343 p_new_positions + start_pos,
2345 p_levels + start_pos );
2346 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2348 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2349 p_old_positions[ j - start_pos ] ];
2350 p_new_positions[ j ] += start_pos;
2354 free( p_old_positions );
2355 free( pp_char_styles );
2356 pp_char_styles = pp_char_styles_new;
2357 psz_text = p_fribidi_string;
2358 p_fribidi_string[ i_len ] = 0;
2361 /* Work out the karaoke */
2362 if( pi_karaoke_bar )
2364 int64_t i_last_duration = 0;
2365 int64_t i_duration = 0;
2366 int64_t i_start_pos = 0;
2367 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2369 for( k = 0; k< i_k_runs; k++ )
2371 double fraction = 0.0;
2373 i_duration += pi_k_durations[ k ];
2375 if( i_duration < i_elapsed )
2377 /* Completely finished this run-length -
2378 * let it render normally */
2382 else if( i_elapsed < i_last_duration )
2384 /* Haven't got up to this segment yet -
2385 * render it completely in karaoke BG mode */
2391 /* Partway through this run */
2393 fraction = (double)(i_elapsed - i_last_duration) /
2394 (double)pi_k_durations[ k ];
2396 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2398 double shade = pi_k_run_lengths[ k ] * fraction;
2400 if( p_new_positions )
2401 j = p_new_positions[ i_start_pos + i ];
2403 j = i_start_pos + i;
2405 if( i < (uint32_t)shade )
2406 pi_karaoke_bar[ j ] = 0xff;
2407 else if( (double)i > shade )
2408 pi_karaoke_bar[ j ] = 0x00;
2411 shade -= (int)shade;
2412 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2413 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2417 i_last_duration = i_duration;
2418 i_start_pos += pi_k_run_lengths[ k ];
2422 free( p_new_positions );
2424 FT_Vector tmp_result;
2426 line_desc_t *p_line = NULL;
2427 line_desc_t *p_prev = NULL;
2433 p_result->x = p_result->y = 0;
2434 tmp_result.x = tmp_result.y = 0;
2437 for( k = 0; k <= (uint32_t) i_len; k++ )
2439 if( ( k == (uint32_t) i_len ) ||
2441 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2443 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2445 /* End of the current style run */
2446 FT_Face p_face = NULL;
2449 /* Look for a match amongst our attachments first */
2450 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2454 char *psz_fontfile = NULL;
2456 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2457 if( p_sys->b_fontconfig_ok )
2459 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2460 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2461 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2462 p_style->psz_fontname,
2467 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2469 if( psz_fontfile && ! *psz_fontfile )
2471 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2472 " so using default font", p_style->psz_fontname,
2473 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2474 (p_style->b_bold ? "(Bold)" :
2475 (p_style->b_italic ? "(Italic)" : ""))) );
2476 free( psz_fontfile );
2477 psz_fontfile = NULL;
2482 if( FT_New_Face( p_sys->p_library,
2483 psz_fontfile, i_idx, &p_face ) )
2485 free( psz_fontfile );
2486 free( pp_char_styles );
2487 #if defined(HAVE_FRIBIDI)
2490 free( pi_karaoke_bar );
2491 return VLC_EGENERIC;
2493 free( psz_fontfile );
2497 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2499 /* We've loaded a font face which is unhelpful for actually
2500 * rendering text - fallback to the default one.
2502 FT_Done_Face( p_face );
2506 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2507 ft_encoding_unicode ) ||
2508 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2509 p_style->i_font_size ) )
2511 if( p_face ) FT_Done_Face( p_face );
2512 free( pp_char_styles );
2513 #if defined(HAVE_FRIBIDI)
2516 free( pi_karaoke_bar );
2517 return VLC_EGENERIC;
2519 p_sys->i_use_kerning =
2520 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2523 uint32_t *psz_unicode = (uint32_t *)
2524 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2527 if( p_face ) FT_Done_Face( p_face );
2528 free( pp_char_styles );
2529 free( psz_unicode );
2530 #if defined(HAVE_FRIBIDI)
2533 free( pi_karaoke_bar );
2536 memcpy( psz_unicode, psz_text + i_prev,
2537 sizeof( uint32_t ) * ( k - i_prev ) );
2538 psz_unicode[ k - i_prev ] = 0;
2539 while( *psz_unicode )
2543 if( !(p_line = NewLine( i_len - i_prev)) )
2545 if( p_face ) FT_Done_Face( p_face );
2546 free( pp_char_styles );
2547 free( psz_unicode );
2548 #if defined(HAVE_FRIBIDI)
2551 free( pi_karaoke_bar );
2554 /* New Color mode only works in YUVA rendering mode --
2555 * (RGB mode has palette constraints on it). We therefore
2556 * need to populate the legacy colour fields also.
2558 p_line->b_new_color_mode = true;
2559 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2560 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2561 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2562 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2563 p_line->p_next = NULL;
2565 i_pen_y += tmp_result.y;
2569 if( p_prev ) p_prev->p_next = p_line;
2570 else *pp_lines = p_line;
2573 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2574 p_style->i_font_color, p_style->b_underline,
2575 p_style->i_karaoke_bg_color,
2576 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2577 &tmp_result ) != VLC_SUCCESS )
2579 if( p_face ) FT_Done_Face( p_face );
2580 free( pp_char_styles );
2581 free( psz_unicode );
2582 #if defined(HAVE_FRIBIDI)
2585 free( pi_karaoke_bar );
2586 return VLC_EGENERIC;
2591 p_result->x = __MAX( p_result->x, tmp_result.x );
2592 p_result->y += tmp_result.y;
2597 if( *psz_unicode == '\n')
2601 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2603 *c_ptr = *(c_ptr+1);
2608 free( psz_unicode );
2609 if( p_face ) FT_Done_Face( p_face );
2613 free( pp_char_styles );
2614 #if defined(HAVE_FRIBIDI)
2619 p_result->x = __MAX( p_result->x, tmp_result.x );
2620 p_result->y += tmp_result.y;
2623 if( pi_karaoke_bar )
2626 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2628 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2630 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2634 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2636 /* 100% BG colour will render faster if we
2637 * instead make it 100% FG colour, so leave
2638 * the ratio alone and copy the value across
2640 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2644 if( pi_karaoke_bar[ i ] & 0x80 )
2646 /* Swap Left and Right sides over for Right aligned
2647 * language text (eg. Arabic, Hebrew)
2649 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2651 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2652 p_line->p_bg_rgb[ k ] = i_tmp;
2654 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2657 /* Jump over the '\n' at the line-end */
2660 free( pi_karaoke_bar );
2666 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2667 subpicture_region_t *p_region_in )
2669 int rv = VLC_SUCCESS;
2670 stream_t *p_sub = NULL;
2671 xml_t *p_xml = NULL;
2672 xml_reader_t *p_xml_reader = NULL;
2674 if( !p_region_in || !p_region_in->psz_html )
2675 return VLC_EGENERIC;
2677 /* Reset the default fontsize in case screen metrics have changed */
2678 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2680 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2681 (uint8_t *) p_region_in->psz_html,
2682 strlen( p_region_in->psz_html ),
2686 p_xml = xml_Create( p_filter );
2689 bool b_karaoke = false;
2691 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2694 /* Look for Root Node */
2695 if( xml_ReaderRead( p_xml_reader ) == 1 )
2697 char *psz_node = xml_ReaderName( p_xml_reader );
2699 if( !strcasecmp( "karaoke", psz_node ) )
2701 /* We're going to have to render the text a number
2702 * of times to show the progress marker on the text.
2704 var_SetBool( p_filter, "text-rerender", true );
2707 else if( !strcasecmp( "text", psz_node ) )
2713 /* Only text and karaoke tags are supported */
2714 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2715 xml_ReaderDelete( p_xml, p_xml_reader );
2716 p_xml_reader = NULL;
2728 uint32_t i_runs = 0;
2729 uint32_t i_k_runs = 0;
2730 uint32_t *pi_run_lengths = NULL;
2731 uint32_t *pi_k_run_lengths = NULL;
2732 uint32_t *pi_k_durations = NULL;
2733 ft_style_t **pp_styles = NULL;
2735 line_desc_t *p_lines = NULL;
2737 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2738 sizeof( uint32_t ) );
2743 rv = ProcessNodes( p_filter, p_xml_reader,
2744 p_region_in->p_style, psz_text, &i_len,
2745 &i_runs, &pi_run_lengths, &pp_styles,
2746 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2749 p_region_out->i_x = p_region_in->i_x;
2750 p_region_out->i_y = p_region_in->i_y;
2752 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2754 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2755 pi_run_lengths, pp_styles, &p_lines, &result,
2756 b_karaoke, i_k_runs, pi_k_run_lengths,
2760 for( k=0; k<i_runs; k++)
2761 DeleteStyle( pp_styles[k] );
2763 free( pi_run_lengths );
2766 /* Don't attempt to render text that couldn't be layed out
2769 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2771 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2773 Render( p_filter, p_region_out, p_lines,
2774 result.x, result.y );
2778 RenderYUVA( p_filter, p_region_out, p_lines,
2779 result.x, result.y );
2783 FreeLines( p_lines );
2785 xml_ReaderDelete( p_xml, p_xml_reader );
2787 xml_Delete( p_xml );
2789 stream_Delete( p_sub );
2795 static char* FontConfig_Select( FcConfig* priv, const char* family,
2796 bool b_bold, bool b_italic, int *i_idx )
2799 FcPattern *pat, *p_pat;
2803 pat = FcPatternCreate();
2804 if (!pat) return NULL;
2806 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2807 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2808 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2809 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2811 FcDefaultSubstitute( pat );
2813 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2815 FcPatternDestroy( pat );
2819 p_pat = FcFontMatch( priv, pat, &result );
2820 FcPatternDestroy( pat );
2821 if( !p_pat ) return NULL;
2823 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2824 || ( val_b != FcTrue ) )
2826 FcPatternDestroy( p_pat );
2829 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2834 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2836 FcPatternDestroy( p_pat );
2841 if( strcasecmp((const char*)val_s, family ) != 0 )
2842 msg_Warn( p_filter, "fontconfig: selected font family is not"
2843 "the requested one: '%s' != '%s'\n",
2844 (const char*)val_s, family );
2847 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2849 FcPatternDestroy( p_pat );
2853 FcPatternDestroy( p_pat );
2854 return strdup( (const char*)val_s );
2858 static void FreeLine( line_desc_t *p_line )
2861 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2863 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2865 free( p_line->pp_glyphs );
2866 free( p_line->p_glyph_pos );
2867 free( p_line->p_fg_rgb );
2868 free( p_line->p_bg_rgb );
2869 free( p_line->p_fg_bg_ratio );
2870 free( p_line->pi_underline_offset );
2871 free( p_line->pi_underline_thickness );
2875 static void FreeLines( line_desc_t *p_lines )
2877 line_desc_t *p_line, *p_next;
2879 if( !p_lines ) return;
2881 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2883 p_next = p_line->p_next;
2888 static line_desc_t *NewLine( int i_count )
2890 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2892 if( !p_line ) return NULL;
2893 p_line->i_height = 0;
2894 p_line->i_width = 0;
2895 p_line->p_next = NULL;
2897 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2898 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2899 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2900 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2901 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2902 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2903 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2904 if( ( p_line->pp_glyphs == NULL ) ||
2905 ( p_line->p_glyph_pos == NULL ) ||
2906 ( p_line->p_fg_rgb == NULL ) ||
2907 ( p_line->p_bg_rgb == NULL ) ||
2908 ( p_line->p_fg_bg_ratio == NULL ) ||
2909 ( p_line->pi_underline_offset == NULL ) ||
2910 ( p_line->pi_underline_thickness == NULL ) )
2912 free( p_line->pi_underline_thickness );
2913 free( p_line->pi_underline_offset );
2914 free( p_line->p_fg_rgb );
2915 free( p_line->p_bg_rgb );
2916 free( p_line->p_fg_bg_ratio );
2917 free( p_line->p_glyph_pos );
2918 free( p_line->pp_glyphs );
2922 p_line->pp_glyphs[0] = NULL;
2923 p_line->b_new_color_mode = false;
2928 static int GetFontSize( filter_t *p_filter )
2930 filter_sys_t *p_sys = p_filter->p_sys;
2934 if( p_sys->i_default_font_size )
2936 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2937 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2939 i_size = p_sys->i_default_font_size;
2943 var_Get( p_filter, "freetype-rel-fontsize", &val );
2944 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2945 p_filter->p_sys->i_display_height =
2946 p_filter->fmt_out.video.i_height;
2950 msg_Warn( p_filter, "invalid fontsize, using 12" );
2951 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2952 i_size = 12 * val.i_int / 1000;
2959 static int SetFontSize( filter_t *p_filter, int i_size )
2961 filter_sys_t *p_sys = p_filter->p_sys;
2965 i_size = GetFontSize( p_filter );
2967 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2970 p_sys->i_font_size = i_size;
2972 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2974 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2975 return VLC_EGENERIC;
2981 static void YUVFromRGB( uint32_t i_argb,
2982 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2984 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2985 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2986 int i_blue = ( i_argb & 0x000000ff );
2988 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2989 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2990 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2991 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2992 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2993 -585 * i_blue + 4096 + 1048576) >> 13, 240);