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 const struct {
1809 const char *psz_name;
1811 } p_html_colors[] = {
1812 /* Official html colors */
1813 { "Aqua", 0x00FFFF },
1814 { "Black", 0x000000 },
1815 { "Blue", 0x0000FF },
1816 { "Fuchsia", 0xFF00FF },
1817 { "Gray", 0x808080 },
1818 { "Green", 0x008000 },
1819 { "Lime", 0x00FF00 },
1820 { "Maroon", 0x800000 },
1821 { "Navy", 0x000080 },
1822 { "Olive", 0x808000 },
1823 { "Purple", 0x800080 },
1824 { "Red", 0xFF0000 },
1825 { "Silver", 0xC0C0C0 },
1826 { "Teal", 0x008080 },
1827 { "White", 0xFFFFFF },
1828 { "Yellow", 0xFFFF00 },
1831 { "AliceBlue", 0xF0F8FF },
1832 { "AntiqueWhite", 0xFAEBD7 },
1833 { "Aqua", 0x00FFFF },
1834 { "Aquamarine", 0x7FFFD4 },
1835 { "Azure", 0xF0FFFF },
1836 { "Beige", 0xF5F5DC },
1837 { "Bisque", 0xFFE4C4 },
1838 { "Black", 0x000000 },
1839 { "BlanchedAlmond", 0xFFEBCD },
1840 { "Blue", 0x0000FF },
1841 { "BlueViolet", 0x8A2BE2 },
1842 { "Brown", 0xA52A2A },
1843 { "BurlyWood", 0xDEB887 },
1844 { "CadetBlue", 0x5F9EA0 },
1845 { "Chartreuse", 0x7FFF00 },
1846 { "Chocolate", 0xD2691E },
1847 { "Coral", 0xFF7F50 },
1848 { "CornflowerBlue", 0x6495ED },
1849 { "Cornsilk", 0xFFF8DC },
1850 { "Crimson", 0xDC143C },
1851 { "Cyan", 0x00FFFF },
1852 { "DarkBlue", 0x00008B },
1853 { "DarkCyan", 0x008B8B },
1854 { "DarkGoldenRod", 0xB8860B },
1855 { "DarkGray", 0xA9A9A9 },
1856 { "DarkGrey", 0xA9A9A9 },
1857 { "DarkGreen", 0x006400 },
1858 { "DarkKhaki", 0xBDB76B },
1859 { "DarkMagenta", 0x8B008B },
1860 { "DarkOliveGreen", 0x556B2F },
1861 { "Darkorange", 0xFF8C00 },
1862 { "DarkOrchid", 0x9932CC },
1863 { "DarkRed", 0x8B0000 },
1864 { "DarkSalmon", 0xE9967A },
1865 { "DarkSeaGreen", 0x8FBC8F },
1866 { "DarkSlateBlue", 0x483D8B },
1867 { "DarkSlateGray", 0x2F4F4F },
1868 { "DarkSlateGrey", 0x2F4F4F },
1869 { "DarkTurquoise", 0x00CED1 },
1870 { "DarkViolet", 0x9400D3 },
1871 { "DeepPink", 0xFF1493 },
1872 { "DeepSkyBlue", 0x00BFFF },
1873 { "DimGray", 0x696969 },
1874 { "DimGrey", 0x696969 },
1875 { "DodgerBlue", 0x1E90FF },
1876 { "FireBrick", 0xB22222 },
1877 { "FloralWhite", 0xFFFAF0 },
1878 { "ForestGreen", 0x228B22 },
1879 { "Fuchsia", 0xFF00FF },
1880 { "Gainsboro", 0xDCDCDC },
1881 { "GhostWhite", 0xF8F8FF },
1882 { "Gold", 0xFFD700 },
1883 { "GoldenRod", 0xDAA520 },
1884 { "Gray", 0x808080 },
1885 { "Grey", 0x808080 },
1886 { "Green", 0x008000 },
1887 { "GreenYellow", 0xADFF2F },
1888 { "HoneyDew", 0xF0FFF0 },
1889 { "HotPink", 0xFF69B4 },
1890 { "IndianRed", 0xCD5C5C },
1891 { "Indigo", 0x4B0082 },
1892 { "Ivory", 0xFFFFF0 },
1893 { "Khaki", 0xF0E68C },
1894 { "Lavender", 0xE6E6FA },
1895 { "LavenderBlush", 0xFFF0F5 },
1896 { "LawnGreen", 0x7CFC00 },
1897 { "LemonChiffon", 0xFFFACD },
1898 { "LightBlue", 0xADD8E6 },
1899 { "LightCoral", 0xF08080 },
1900 { "LightCyan", 0xE0FFFF },
1901 { "LightGoldenRodYellow", 0xFAFAD2 },
1902 { "LightGray", 0xD3D3D3 },
1903 { "LightGrey", 0xD3D3D3 },
1904 { "LightGreen", 0x90EE90 },
1905 { "LightPink", 0xFFB6C1 },
1906 { "LightSalmon", 0xFFA07A },
1907 { "LightSeaGreen", 0x20B2AA },
1908 { "LightSkyBlue", 0x87CEFA },
1909 { "LightSlateGray", 0x778899 },
1910 { "LightSlateGrey", 0x778899 },
1911 { "LightSteelBlue", 0xB0C4DE },
1912 { "LightYellow", 0xFFFFE0 },
1913 { "Lime", 0x00FF00 },
1914 { "LimeGreen", 0x32CD32 },
1915 { "Linen", 0xFAF0E6 },
1916 { "Magenta", 0xFF00FF },
1917 { "Maroon", 0x800000 },
1918 { "MediumAquaMarine", 0x66CDAA },
1919 { "MediumBlue", 0x0000CD },
1920 { "MediumOrchid", 0xBA55D3 },
1921 { "MediumPurple", 0x9370D8 },
1922 { "MediumSeaGreen", 0x3CB371 },
1923 { "MediumSlateBlue", 0x7B68EE },
1924 { "MediumSpringGreen", 0x00FA9A },
1925 { "MediumTurquoise", 0x48D1CC },
1926 { "MediumVioletRed", 0xC71585 },
1927 { "MidnightBlue", 0x191970 },
1928 { "MintCream", 0xF5FFFA },
1929 { "MistyRose", 0xFFE4E1 },
1930 { "Moccasin", 0xFFE4B5 },
1931 { "NavajoWhite", 0xFFDEAD },
1932 { "Navy", 0x000080 },
1933 { "OldLace", 0xFDF5E6 },
1934 { "Olive", 0x808000 },
1935 { "OliveDrab", 0x6B8E23 },
1936 { "Orange", 0xFFA500 },
1937 { "OrangeRed", 0xFF4500 },
1938 { "Orchid", 0xDA70D6 },
1939 { "PaleGoldenRod", 0xEEE8AA },
1940 { "PaleGreen", 0x98FB98 },
1941 { "PaleTurquoise", 0xAFEEEE },
1942 { "PaleVioletRed", 0xD87093 },
1943 { "PapayaWhip", 0xFFEFD5 },
1944 { "PeachPuff", 0xFFDAB9 },
1945 { "Peru", 0xCD853F },
1946 { "Pink", 0xFFC0CB },
1947 { "Plum", 0xDDA0DD },
1948 { "PowderBlue", 0xB0E0E6 },
1949 { "Purple", 0x800080 },
1950 { "Red", 0xFF0000 },
1951 { "RosyBrown", 0xBC8F8F },
1952 { "RoyalBlue", 0x4169E1 },
1953 { "SaddleBrown", 0x8B4513 },
1954 { "Salmon", 0xFA8072 },
1955 { "SandyBrown", 0xF4A460 },
1956 { "SeaGreen", 0x2E8B57 },
1957 { "SeaShell", 0xFFF5EE },
1958 { "Sienna", 0xA0522D },
1959 { "Silver", 0xC0C0C0 },
1960 { "SkyBlue", 0x87CEEB },
1961 { "SlateBlue", 0x6A5ACD },
1962 { "SlateGray", 0x708090 },
1963 { "SlateGrey", 0x708090 },
1964 { "Snow", 0xFFFAFA },
1965 { "SpringGreen", 0x00FF7F },
1966 { "SteelBlue", 0x4682B4 },
1967 { "Tan", 0xD2B48C },
1968 { "Teal", 0x008080 },
1969 { "Thistle", 0xD8BFD8 },
1970 { "Tomato", 0xFF6347 },
1971 { "Turquoise", 0x40E0D0 },
1972 { "Violet", 0xEE82EE },
1973 { "Wheat", 0xF5DEB3 },
1974 { "White", 0xFFFFFF },
1975 { "WhiteSmoke", 0xF5F5F5 },
1976 { "Yellow", 0xFFFF00 },
1977 { "YellowGreen", 0x9ACD32 },
1982 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1983 font_stack_t **p_fonts, int i_scale )
1986 char *psz_fontname = NULL;
1987 uint32_t i_font_color = 0xffffff;
1988 int i_font_alpha = 0;
1989 uint32_t i_karaoke_bg_color = 0x00ffffff;
1990 int i_font_size = 24;
1992 /* Default all attributes to the top font in the stack -- in case not
1993 * all attributes are specified in the sub-font
1995 if( VLC_SUCCESS == PeekFont( p_fonts,
1999 &i_karaoke_bg_color ))
2001 psz_fontname = strdup( psz_fontname );
2002 i_font_size = i_font_size * 1000 / i_scale;
2004 i_font_alpha = (i_font_color >> 24) & 0xff;
2005 i_font_color &= 0x00ffffff;
2007 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
2009 char *psz_name = xml_ReaderName( p_xml_reader );
2010 char *psz_value = xml_ReaderValue( p_xml_reader );
2012 if( psz_name && psz_value )
2014 if( !strcasecmp( "face", psz_name ) )
2016 free( psz_fontname );
2017 psz_fontname = strdup( psz_value );
2019 else if( !strcasecmp( "size", psz_name ) )
2021 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
2023 int i_value = atoi( psz_value );
2025 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
2026 i_font_size += ( i_value * i_font_size ) / 10;
2027 else if( i_value < -5 )
2028 i_font_size = - i_value;
2029 else if( i_value > 5 )
2030 i_font_size = i_value;
2033 i_font_size = atoi( psz_value );
2035 else if( !strcasecmp( "color", psz_name ) )
2037 if( psz_value[0] == '#' )
2039 i_font_color = strtol( psz_value + 1, NULL, 16 );
2040 i_font_color &= 0x00ffffff;
2044 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
2046 if( !strncasecmp( psz_value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
2048 i_font_color = p_html_colors[i].i_value;
2054 else if( !strcasecmp( "alpha", psz_name ) &&
2055 ( psz_value[0] == '#' ) )
2057 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
2058 i_font_alpha &= 0xff;
2064 rv = PushFont( p_fonts,
2066 i_font_size * i_scale / 1000,
2067 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
2068 i_karaoke_bg_color );
2070 free( psz_fontname );
2075 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2076 uint32_t **psz_text_out, uint32_t *pi_runs,
2077 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2078 ft_style_t *p_style )
2080 uint32_t i_string_length = 0;
2082 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
2083 *psz_text_out += i_string_length;
2085 if( ppp_styles && ppi_run_lengths )
2091 *ppp_styles = (ft_style_t **)
2092 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
2094 else if( *pi_runs == 1 )
2096 *ppp_styles = (ft_style_t **)
2097 malloc( *pi_runs * sizeof( ft_style_t * ) );
2100 /* We have just malloc'ed this memory successfully -
2101 * *pi_runs HAS to be within the memory area of *ppp_styles */
2104 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
2108 if( *ppi_run_lengths )
2110 *ppi_run_lengths = (uint32_t *)
2111 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
2113 else if( *pi_runs == 1 )
2115 *ppi_run_lengths = (uint32_t *)
2116 malloc( *pi_runs * sizeof( uint32_t ) );
2119 /* same remarks here */
2120 if( *ppi_run_lengths )
2122 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
2125 /* If we couldn't use the p_style argument due to memory allocation
2126 * problems above, release it here.
2128 if( p_style ) DeleteStyle( p_style );
2131 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
2132 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
2134 /* Karaoke tags _PRECEDE_ the text they specify a duration
2135 * for, therefore we are working out the length for the
2136 * previous tag, and first time through we have nothing
2138 if( pi_k_run_lengths )
2143 /* Work out how many characters are presently in the string
2145 for( i = 0; i < i_runs; i++ )
2146 i_chars += pi_run_lengths[ i ];
2148 /* Subtract away those we've already allocated to other
2151 for( i = 0; i < i_k_runs; i++ )
2152 i_chars -= pi_k_run_lengths[ i ];
2154 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
2158 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
2159 uint32_t **ppi_k_run_lengths,
2160 uint32_t **ppi_k_durations )
2162 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
2164 char *psz_name = xml_ReaderName( p_xml_reader );
2165 char *psz_value = xml_ReaderValue( p_xml_reader );
2167 if( psz_name && psz_value &&
2168 !strcasecmp( "t", psz_name ) )
2170 if( ppi_k_durations && ppi_k_run_lengths )
2174 if( *ppi_k_durations )
2176 *ppi_k_durations = (uint32_t *)
2177 realloc( *ppi_k_durations,
2178 *pi_k_runs * sizeof( uint32_t ) );
2180 else if( *pi_k_runs == 1 )
2182 *ppi_k_durations = (uint32_t *)
2183 malloc( *pi_k_runs * sizeof( uint32_t ) );
2186 if( *ppi_k_run_lengths )
2188 *ppi_k_run_lengths = (uint32_t *)
2189 realloc( *ppi_k_run_lengths,
2190 *pi_k_runs * sizeof( uint32_t ) );
2192 else if( *pi_k_runs == 1 )
2194 *ppi_k_run_lengths = (uint32_t *)
2195 malloc( *pi_k_runs * sizeof( uint32_t ) );
2197 if( *ppi_k_durations )
2198 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
2200 if( *ppi_k_run_lengths )
2201 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
2209 static int ProcessNodes( filter_t *p_filter,
2210 xml_reader_t *p_xml_reader,
2211 text_style_t *p_font_style,
2216 uint32_t **ppi_run_lengths,
2217 ft_style_t ***ppp_styles,
2220 uint32_t *pi_k_runs,
2221 uint32_t **ppi_k_run_lengths,
2222 uint32_t **ppi_k_durations )
2224 int rv = VLC_SUCCESS;
2225 filter_sys_t *p_sys = p_filter->p_sys;
2226 uint32_t *psz_text_orig = psz_text;
2227 font_stack_t *p_fonts = NULL;
2231 char *psz_node = NULL;
2233 bool b_italic = false;
2234 bool b_bold = false;
2235 bool b_uline = false;
2237 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2238 i_scale = val.i_int;
2242 rv = PushFont( &p_fonts,
2243 p_font_style->psz_fontname,
2244 p_font_style->i_font_size * i_scale / 1000,
2245 (p_font_style->i_font_color & 0xffffff) |
2246 ((p_font_style->i_font_alpha & 0xff) << 24),
2247 (p_font_style->i_karaoke_background_color & 0xffffff) |
2248 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2250 if( p_font_style->i_style_flags & STYLE_BOLD )
2252 if( p_font_style->i_style_flags & STYLE_ITALIC )
2254 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2259 rv = PushFont( &p_fonts,
2265 if( rv != VLC_SUCCESS )
2268 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2270 switch ( xml_ReaderNodeType( p_xml_reader ) )
2272 case XML_READER_NONE:
2274 case XML_READER_ENDELEM:
2275 psz_node = xml_ReaderName( p_xml_reader );
2279 if( !strcasecmp( "font", psz_node ) )
2280 PopFont( &p_fonts );
2281 else if( !strcasecmp( "b", psz_node ) )
2283 else if( !strcasecmp( "i", psz_node ) )
2285 else if( !strcasecmp( "u", psz_node ) )
2291 case XML_READER_STARTELEM:
2292 psz_node = xml_ReaderName( p_xml_reader );
2295 if( !strcasecmp( "font", psz_node ) )
2296 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2297 else if( !strcasecmp( "b", psz_node ) )
2299 else if( !strcasecmp( "i", psz_node ) )
2301 else if( !strcasecmp( "u", psz_node ) )
2303 else if( !strcasecmp( "br", psz_node ) )
2305 SetupLine( p_filter, "\n", &psz_text,
2306 pi_runs, ppi_run_lengths, ppp_styles,
2307 GetStyleFromFontStack( p_sys,
2313 else if( !strcasecmp( "k", psz_node ) )
2315 /* Only valid in karaoke */
2318 if( *pi_k_runs > 0 )
2320 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2321 *pi_k_runs, *ppi_k_run_lengths );
2323 SetupKaraoke( p_xml_reader, pi_k_runs,
2324 ppi_k_run_lengths, ppi_k_durations );
2331 case XML_READER_TEXT:
2332 psz_node = xml_ReaderValue( p_xml_reader );
2335 /* Turn any multiple-whitespaces into single spaces */
2336 char *s = strpbrk( psz_node, "\t\r\n " );
2339 int i_whitespace = strspn( s, "\t\r\n " );
2341 if( i_whitespace > 1 )
2344 strlen( s ) - i_whitespace + 1 );
2347 s = strpbrk( s, "\t\r\n " );
2349 SetupLine( p_filter, psz_node, &psz_text,
2350 pi_runs, ppi_run_lengths, ppp_styles,
2351 GetStyleFromFontStack( p_sys,
2360 if( rv != VLC_SUCCESS )
2362 psz_text = psz_text_orig;
2368 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2369 *pi_k_runs, *ppi_k_run_lengths );
2372 *pi_len = psz_text - psz_text_orig;
2374 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2379 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2383 for( k=0; k < p_sys->i_font_attachments; k++ )
2385 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2387 FT_Face p_face = NULL;
2389 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2397 bool match = !strcasecmp( p_face->family_name,
2398 p_style->psz_fontname );
2400 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2401 match = match && p_style->b_bold;
2403 match = match && !p_style->b_bold;
2405 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2406 match = match && p_style->b_italic;
2408 match = match && !p_style->b_italic;
2416 FT_Done_Face( p_face );
2421 return VLC_EGENERIC;
2424 static int ProcessLines( filter_t *p_filter,
2429 uint32_t *pi_run_lengths,
2430 ft_style_t **pp_styles,
2431 line_desc_t **pp_lines,
2433 FT_Vector *p_result,
2437 uint32_t *pi_k_run_lengths,
2438 uint32_t *pi_k_durations )
2440 filter_sys_t *p_sys = p_filter->p_sys;
2441 ft_style_t **pp_char_styles;
2442 int *p_new_positions = NULL;
2443 int8_t *p_levels = NULL;
2444 uint8_t *pi_karaoke_bar = NULL;
2448 /* Assign each character in the text string its style explicitly, so that
2449 * after the characters have been shuffled around by Fribidi, we can re-apply
2450 * the styles, and to simplify the calculation of runs within a line.
2452 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2453 if( !pp_char_styles )
2458 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2459 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2460 * we just won't be able to display the progress bar; at least we'll
2466 for( j = 0; j < i_runs; j++ )
2467 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2468 pp_char_styles[ i++ ] = pp_styles[ j ];
2470 #if defined(HAVE_FRIBIDI)
2472 ft_style_t **pp_char_styles_new;
2473 int *p_old_positions;
2474 uint32_t *p_fribidi_string;
2475 int start_pos, pos = 0;
2477 pp_char_styles_new = (ft_style_t **)
2478 malloc( i_len * sizeof( ft_style_t * ));
2480 p_fribidi_string = (uint32_t *)
2481 malloc( (i_len + 1) * sizeof(uint32_t) );
2482 p_old_positions = (int *)
2483 malloc( (i_len + 1) * sizeof( int ) );
2484 p_new_positions = (int *)
2485 malloc( (i_len + 1) * sizeof( int ) );
2486 p_levels = (int8_t *)
2487 malloc( (i_len + 1) * sizeof( int8_t ) );
2489 if( ! pp_char_styles_new ||
2490 ! p_fribidi_string ||
2491 ! p_old_positions ||
2492 ! p_new_positions ||
2496 free( p_old_positions );
2497 free( p_new_positions );
2498 free( p_fribidi_string );
2499 free( pp_char_styles_new );
2500 free( pi_karaoke_bar );
2502 free( pp_char_styles );
2506 /* Do bidi conversion line-by-line */
2509 while(pos < i_len) {
2510 if (psz_text[pos] != '\n')
2512 p_fribidi_string[pos] = psz_text[pos];
2513 pp_char_styles_new[pos] = pp_char_styles[pos];
2514 p_new_positions[pos] = pos;
2519 while(pos < i_len) {
2520 if (psz_text[pos] == '\n')
2524 if (pos > start_pos)
2526 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2527 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2528 pos - start_pos, &base_dir,
2529 (FriBidiChar*)p_fribidi_string + start_pos,
2530 p_new_positions + start_pos,
2532 p_levels + start_pos );
2533 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2535 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2536 p_old_positions[ j - start_pos ] ];
2537 p_new_positions[ j ] += start_pos;
2541 free( p_old_positions );
2542 free( pp_char_styles );
2543 pp_char_styles = pp_char_styles_new;
2544 psz_text = p_fribidi_string;
2545 p_fribidi_string[ i_len ] = 0;
2548 /* Work out the karaoke */
2549 if( pi_karaoke_bar )
2551 int64_t i_last_duration = 0;
2552 int64_t i_duration = 0;
2553 int64_t i_start_pos = 0;
2554 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2556 for( k = 0; k< i_k_runs; k++ )
2558 double fraction = 0.0;
2560 i_duration += pi_k_durations[ k ];
2562 if( i_duration < i_elapsed )
2564 /* Completely finished this run-length -
2565 * let it render normally */
2569 else if( i_elapsed < i_last_duration )
2571 /* Haven't got up to this segment yet -
2572 * render it completely in karaoke BG mode */
2578 /* Partway through this run */
2580 fraction = (double)(i_elapsed - i_last_duration) /
2581 (double)pi_k_durations[ k ];
2583 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2585 double shade = pi_k_run_lengths[ k ] * fraction;
2587 if( p_new_positions )
2588 j = p_new_positions[ i_start_pos + i ];
2590 j = i_start_pos + i;
2592 if( i < (uint32_t)shade )
2593 pi_karaoke_bar[ j ] = 0xff;
2594 else if( (double)i > shade )
2595 pi_karaoke_bar[ j ] = 0x00;
2598 shade -= (int)shade;
2599 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2600 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2604 i_last_duration = i_duration;
2605 i_start_pos += pi_k_run_lengths[ k ];
2609 free( p_new_positions );
2611 FT_Vector tmp_result;
2613 line_desc_t *p_line = NULL;
2614 line_desc_t *p_prev = NULL;
2620 p_result->x = p_result->y = 0;
2621 tmp_result.x = tmp_result.y = 0;
2624 for( k = 0; k <= (uint32_t) i_len; k++ )
2626 if( ( k == (uint32_t) i_len ) ||
2628 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2630 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2632 /* End of the current style run */
2633 FT_Face p_face = NULL;
2636 /* Look for a match amongst our attachments first */
2637 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2641 char *psz_fontfile = NULL;
2643 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2644 if( p_sys->b_fontconfig_ok )
2646 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2647 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2648 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2649 p_style->psz_fontname,
2654 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2656 if( psz_fontfile && ! *psz_fontfile )
2658 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2659 " so using default font", p_style->psz_fontname,
2660 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2661 (p_style->b_bold ? "(Bold)" :
2662 (p_style->b_italic ? "(Italic)" : ""))) );
2663 free( psz_fontfile );
2664 psz_fontfile = NULL;
2669 if( FT_New_Face( p_sys->p_library,
2670 psz_fontfile, i_idx, &p_face ) )
2672 free( psz_fontfile );
2673 free( pp_char_styles );
2674 #if defined(HAVE_FRIBIDI)
2677 free( pi_karaoke_bar );
2678 return VLC_EGENERIC;
2680 free( psz_fontfile );
2684 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2686 /* We've loaded a font face which is unhelpful for actually
2687 * rendering text - fallback to the default one.
2689 FT_Done_Face( p_face );
2693 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2694 ft_encoding_unicode ) ||
2695 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2696 p_style->i_font_size ) )
2698 if( p_face ) FT_Done_Face( p_face );
2699 free( pp_char_styles );
2700 #if defined(HAVE_FRIBIDI)
2703 free( pi_karaoke_bar );
2704 return VLC_EGENERIC;
2706 p_sys->i_use_kerning =
2707 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2710 uint32_t *psz_unicode = (uint32_t *)
2711 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2714 if( p_face ) FT_Done_Face( p_face );
2715 free( pp_char_styles );
2716 free( psz_unicode );
2717 #if defined(HAVE_FRIBIDI)
2720 free( pi_karaoke_bar );
2723 memcpy( psz_unicode, psz_text + i_prev,
2724 sizeof( uint32_t ) * ( k - i_prev ) );
2725 psz_unicode[ k - i_prev ] = 0;
2726 while( *psz_unicode )
2730 if( !(p_line = NewLine( i_len - i_prev)) )
2732 if( p_face ) FT_Done_Face( p_face );
2733 free( pp_char_styles );
2734 free( psz_unicode );
2735 #if defined(HAVE_FRIBIDI)
2738 free( pi_karaoke_bar );
2741 /* New Color mode only works in YUVA rendering mode --
2742 * (RGB mode has palette constraints on it). We therefore
2743 * need to populate the legacy colour fields also.
2745 p_line->b_new_color_mode = true;
2746 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2747 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2748 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2749 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2750 p_line->p_next = NULL;
2752 i_pen_y += tmp_result.y;
2756 if( p_prev ) p_prev->p_next = p_line;
2757 else *pp_lines = p_line;
2760 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2761 p_style->i_font_color, p_style->b_underline,
2762 p_style->i_karaoke_bg_color,
2763 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2764 &tmp_result ) != VLC_SUCCESS )
2766 if( p_face ) FT_Done_Face( p_face );
2767 free( pp_char_styles );
2768 free( psz_unicode );
2769 #if defined(HAVE_FRIBIDI)
2772 free( pi_karaoke_bar );
2773 return VLC_EGENERIC;
2778 p_result->x = __MAX( p_result->x, tmp_result.x );
2779 p_result->y += tmp_result.y;
2784 if( *psz_unicode == '\n')
2788 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2790 *c_ptr = *(c_ptr+1);
2795 free( psz_unicode );
2796 if( p_face ) FT_Done_Face( p_face );
2800 free( pp_char_styles );
2801 #if defined(HAVE_FRIBIDI)
2806 p_result->x = __MAX( p_result->x, tmp_result.x );
2807 p_result->y += tmp_result.y;
2810 if( pi_karaoke_bar )
2813 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2815 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2817 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2821 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2823 /* 100% BG colour will render faster if we
2824 * instead make it 100% FG colour, so leave
2825 * the ratio alone and copy the value across
2827 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2831 if( pi_karaoke_bar[ i ] & 0x80 )
2833 /* Swap Left and Right sides over for Right aligned
2834 * language text (eg. Arabic, Hebrew)
2836 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2838 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2839 p_line->p_bg_rgb[ k ] = i_tmp;
2841 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2844 /* Jump over the '\n' at the line-end */
2847 free( pi_karaoke_bar );
2853 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2854 subpicture_region_t *p_region_in )
2856 int rv = VLC_SUCCESS;
2857 stream_t *p_sub = NULL;
2858 xml_t *p_xml = NULL;
2859 xml_reader_t *p_xml_reader = NULL;
2861 if( !p_region_in || !p_region_in->psz_html )
2862 return VLC_EGENERIC;
2864 /* Reset the default fontsize in case screen metrics have changed */
2865 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2867 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2868 (uint8_t *) p_region_in->psz_html,
2869 strlen( p_region_in->psz_html ),
2873 p_xml = xml_Create( p_filter );
2876 bool b_karaoke = false;
2878 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2881 /* Look for Root Node */
2882 if( xml_ReaderRead( p_xml_reader ) == 1 )
2884 char *psz_node = xml_ReaderName( p_xml_reader );
2886 if( !strcasecmp( "karaoke", psz_node ) )
2888 /* We're going to have to render the text a number
2889 * of times to show the progress marker on the text.
2891 var_SetBool( p_filter, "text-rerender", true );
2894 else if( !strcasecmp( "text", psz_node ) )
2900 /* Only text and karaoke tags are supported */
2901 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2902 xml_ReaderDelete( p_xml, p_xml_reader );
2903 p_xml_reader = NULL;
2915 uint32_t i_runs = 0;
2916 uint32_t i_k_runs = 0;
2917 uint32_t *pi_run_lengths = NULL;
2918 uint32_t *pi_k_run_lengths = NULL;
2919 uint32_t *pi_k_durations = NULL;
2920 ft_style_t **pp_styles = NULL;
2922 line_desc_t *p_lines = NULL;
2924 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2925 sizeof( uint32_t ) );
2930 rv = ProcessNodes( p_filter, p_xml_reader,
2931 p_region_in->p_style, psz_text, &i_len,
2932 &i_runs, &pi_run_lengths, &pp_styles,
2933 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2936 p_region_out->i_x = p_region_in->i_x;
2937 p_region_out->i_y = p_region_in->i_y;
2939 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2941 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2942 pi_run_lengths, pp_styles, &p_lines, &result,
2943 b_karaoke, i_k_runs, pi_k_run_lengths,
2947 for( k=0; k<i_runs; k++)
2948 DeleteStyle( pp_styles[k] );
2950 free( pi_run_lengths );
2953 /* Don't attempt to render text that couldn't be layed out
2956 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2958 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2960 Render( p_filter, p_region_out, p_lines,
2961 result.x, result.y );
2965 RenderYUVA( p_filter, p_region_out, p_lines,
2966 result.x, result.y );
2970 FreeLines( p_lines );
2972 xml_ReaderDelete( p_xml, p_xml_reader );
2974 xml_Delete( p_xml );
2976 stream_Delete( p_sub );
2982 static char* FontConfig_Select( FcConfig* priv, const char* family,
2983 bool b_bold, bool b_italic, int *i_idx )
2986 FcPattern *pat, *p_pat;
2990 pat = FcPatternCreate();
2991 if (!pat) return NULL;
2993 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2994 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2995 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2996 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2998 FcDefaultSubstitute( pat );
3000 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
3002 FcPatternDestroy( pat );
3006 p_pat = FcFontMatch( priv, pat, &result );
3007 FcPatternDestroy( pat );
3008 if( !p_pat ) return NULL;
3010 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
3011 || ( val_b != FcTrue ) )
3013 FcPatternDestroy( p_pat );
3016 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
3021 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
3023 FcPatternDestroy( p_pat );
3028 if( strcasecmp((const char*)val_s, family ) != 0 )
3029 msg_Warn( p_filter, "fontconfig: selected font family is not"
3030 "the requested one: '%s' != '%s'\n",
3031 (const char*)val_s, family );
3034 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
3036 FcPatternDestroy( p_pat );
3040 FcPatternDestroy( p_pat );
3041 return strdup( (const char*)val_s );
3045 static void FreeLine( line_desc_t *p_line )
3048 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
3050 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
3052 free( p_line->pp_glyphs );
3053 free( p_line->p_glyph_pos );
3054 free( p_line->p_fg_rgb );
3055 free( p_line->p_bg_rgb );
3056 free( p_line->p_fg_bg_ratio );
3057 free( p_line->pi_underline_offset );
3058 free( p_line->pi_underline_thickness );
3062 static void FreeLines( line_desc_t *p_lines )
3064 line_desc_t *p_line, *p_next;
3066 if( !p_lines ) return;
3068 for( p_line = p_lines; p_line != NULL; p_line = p_next )
3070 p_next = p_line->p_next;
3075 static line_desc_t *NewLine( int i_count )
3077 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
3079 if( !p_line ) return NULL;
3080 p_line->i_height = 0;
3081 p_line->i_width = 0;
3082 p_line->p_next = NULL;
3084 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
3085 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
3086 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
3087 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
3088 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
3089 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
3090 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
3091 if( ( p_line->pp_glyphs == NULL ) ||
3092 ( p_line->p_glyph_pos == NULL ) ||
3093 ( p_line->p_fg_rgb == NULL ) ||
3094 ( p_line->p_bg_rgb == NULL ) ||
3095 ( p_line->p_fg_bg_ratio == NULL ) ||
3096 ( p_line->pi_underline_offset == NULL ) ||
3097 ( p_line->pi_underline_thickness == NULL ) )
3099 free( p_line->pi_underline_thickness );
3100 free( p_line->pi_underline_offset );
3101 free( p_line->p_fg_rgb );
3102 free( p_line->p_bg_rgb );
3103 free( p_line->p_fg_bg_ratio );
3104 free( p_line->p_glyph_pos );
3105 free( p_line->pp_glyphs );
3109 p_line->pp_glyphs[0] = NULL;
3110 p_line->b_new_color_mode = false;
3115 static int GetFontSize( filter_t *p_filter )
3117 filter_sys_t *p_sys = p_filter->p_sys;
3121 if( p_sys->i_default_font_size )
3123 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
3124 i_size = p_sys->i_default_font_size * val.i_int / 1000;
3126 i_size = p_sys->i_default_font_size;
3130 var_Get( p_filter, "freetype-rel-fontsize", &val );
3133 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
3134 p_filter->p_sys->i_display_height =
3135 p_filter->fmt_out.video.i_height;
3140 msg_Warn( p_filter, "invalid fontsize, using 12" );
3141 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
3142 i_size = 12 * val.i_int / 1000;
3149 static int SetFontSize( filter_t *p_filter, int i_size )
3151 filter_sys_t *p_sys = p_filter->p_sys;
3155 i_size = GetFontSize( p_filter );
3157 msg_Dbg( p_filter, "using fontsize: %i", i_size );
3160 p_sys->i_font_size = i_size;
3162 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
3164 msg_Err( p_filter, "couldn't set font size to %d", i_size );
3165 return VLC_EGENERIC;
3171 static void YUVFromRGB( uint32_t i_argb,
3172 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
3174 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
3175 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
3176 int i_blue = ( i_argb & 0x000000ff );
3178 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
3179 802 * i_blue + 4096 + 131072 ) >> 13, 235);
3180 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
3181 3598 * i_blue + 4096 + 1048576) >> 13, 240);
3182 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
3183 -585 * i_blue + 4096 + 1048576) >> 13, 240);