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 */
666 /* Create a new subpicture region */
667 memset( &fmt, 0, sizeof(video_format_t) );
668 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
670 fmt.i_width = fmt.i_visible_width = i_width + 4;
671 fmt.i_height = fmt.i_visible_height = i_height + 4;
672 if( p_region->fmt.i_visible_width > 0 )
673 fmt.i_visible_width = p_region->fmt.i_visible_width;
674 if( p_region->fmt.i_visible_height > 0 )
675 fmt.i_visible_height = p_region->fmt.i_visible_height;
676 fmt.i_x_offset = fmt.i_y_offset = 0;
678 assert( !p_region->p_picture );
679 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
680 if( !p_region->p_picture )
684 /* Calculate text color components */
685 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
686 25 * p_line->i_blue + 128) >> 8) + 16;
687 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
688 112 * p_line->i_blue + 128) >> 8) + 128;
689 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
690 18 * p_line->i_blue + 128) >> 8) + 128;
693 fmt.p_palette->i_entries = 16;
694 for( i = 0; i < 8; i++ )
696 fmt.p_palette->palette[i][0] = 0;
697 fmt.p_palette->palette[i][1] = 0x80;
698 fmt.p_palette->palette[i][2] = 0x80;
699 fmt.p_palette->palette[i][3] = pi_gamma[i];
700 fmt.p_palette->palette[i][3] =
701 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
703 for( i = 8; i < fmt.p_palette->i_entries; i++ )
705 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
706 fmt.p_palette->palette[i][1] = i_u;
707 fmt.p_palette->palette[i][2] = i_v;
708 fmt.p_palette->palette[i][3] = pi_gamma[i];
709 fmt.p_palette->palette[i][3] =
710 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
713 p_dst = p_region->p_picture->Y_PIXELS;
714 i_pitch = p_region->p_picture->Y_PITCH;
716 /* Initialize the region pixels */
717 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
719 for( ; p_line != NULL; p_line = p_line->p_next )
721 int i_glyph_tmax = 0;
722 int i_bitmap_offset, i_offset, i_align_offset = 0;
723 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
725 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
726 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
729 if( p_line->i_width < i_width )
731 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
733 i_align_offset = i_width - p_line->i_width;
735 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
737 i_align_offset = ( i_width - p_line->i_width ) / 2;
741 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
743 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
745 i_offset = ( p_line->p_glyph_pos[ i ].y +
746 i_glyph_tmax - p_glyph->top + 2 ) *
747 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
750 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
752 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
754 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
756 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
763 /* Outlining (find something better than nearest neighbour filtering ?) */
766 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
767 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
768 uint8_t left, current;
770 for( y = 1; y < (int)fmt.i_height - 1; y++ )
772 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
773 p_dst += p_region->p_picture->Y_PITCH;
776 for( x = 1; x < (int)fmt.i_width - 1; x++ )
779 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
780 p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
784 memset( p_top, 0, fmt.i_width );
790 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
791 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
792 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
793 int i_glyph_tmax, int i_align_offset,
794 uint8_t i_y, uint8_t i_u, uint8_t i_v,
795 subpicture_region_t *p_region)
799 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
801 p_dst_y = p_region->p_picture->Y_PIXELS;
802 p_dst_u = p_region->p_picture->U_PIXELS;
803 p_dst_v = p_region->p_picture->V_PIXELS;
804 p_dst_a = p_region->p_picture->A_PIXELS;
805 i_pitch = p_region->p_picture->A_PITCH;
807 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
808 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
810 for( y = 0; y < i_line_thickness; y++ )
812 int i_extra = p_this_glyph->bitmap.width;
816 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
817 (p_this_glyph_pos->x + p_this_glyph->left);
819 for( x = 0; x < i_extra; x++ )
823 /* break the underline around the tails of any glyphs which cross it */
824 for( z = x - i_line_thickness;
825 z < x + i_line_thickness && b_ok;
828 if( p_next_glyph && ( z >= i_extra ) )
830 int i_row = i_line_offset + p_next_glyph->top + y;
832 if( ( p_next_glyph->bitmap.rows > i_row ) &&
833 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
838 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
840 int i_row = i_line_offset + p_this_glyph->top + y;
842 if( ( p_this_glyph->bitmap.rows > i_row ) &&
843 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
852 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
853 p_dst_u[i_offset+x] = i_u;
854 p_dst_v[i_offset+x] = i_v;
855 p_dst_a[i_offset+x] = 255;
862 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
864 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
865 int i_pitch = p_region->p_picture->A_PITCH;
868 for( ; p_line != NULL; p_line = p_line->p_next )
870 int i_glyph_tmax=0, i = 0;
871 int i_bitmap_offset, i_offset, i_align_offset = 0;
872 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
874 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
875 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
878 if( p_line->i_width < i_width )
880 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
882 i_align_offset = i_width - p_line->i_width;
884 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
886 i_align_offset = ( i_width - p_line->i_width ) / 2;
890 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
892 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
894 i_offset = ( p_line->p_glyph_pos[ i ].y +
895 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
896 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
897 i_align_offset +xoffset;
899 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
901 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
903 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
904 if( p_dst[i_offset+x] <
905 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
907 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
916 /*****************************************************************************
917 * Render: place string in picture
918 *****************************************************************************
919 * This function merges the previously rendered freetype glyphs into a picture
920 *****************************************************************************/
921 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
922 line_desc_t *p_line, int i_width, int i_height )
924 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
926 int i, x, y, i_pitch, i_alpha;
927 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
929 if( i_width == 0 || i_height == 0 )
932 /* Create a new subpicture region */
933 memset( &fmt, 0, sizeof(video_format_t) );
934 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
936 fmt.i_width = fmt.i_visible_width = i_width + 6;
937 fmt.i_height = fmt.i_visible_height = i_height + 6;
938 if( p_region->fmt.i_visible_width > 0 )
939 fmt.i_visible_width = p_region->fmt.i_visible_width;
940 if( p_region->fmt.i_visible_height > 0 )
941 fmt.i_visible_height = p_region->fmt.i_visible_height;
942 fmt.i_x_offset = fmt.i_y_offset = 0;
944 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
945 if( !p_region->p_picture )
949 /* Calculate text color components */
950 YUVFromRGB( (p_line->i_red << 16) |
951 (p_line->i_green << 8) |
954 i_alpha = p_line->i_alpha;
956 p_dst_y = p_region->p_picture->Y_PIXELS;
957 p_dst_u = p_region->p_picture->U_PIXELS;
958 p_dst_v = p_region->p_picture->V_PIXELS;
959 p_dst_a = p_region->p_picture->A_PIXELS;
960 i_pitch = p_region->p_picture->A_PITCH;
962 /* Initialize the region pixels */
963 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
965 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
966 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
967 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
968 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
972 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
973 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
974 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
975 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
977 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
978 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
980 DrawBlack( p_line, i_width, p_region, 0, 0);
981 DrawBlack( p_line, i_width, p_region, -1, 0);
982 DrawBlack( p_line, i_width, p_region, 0, -1);
983 DrawBlack( p_line, i_width, p_region, 1, 0);
984 DrawBlack( p_line, i_width, p_region, 0, 1);
987 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
989 DrawBlack( p_line, i_width, p_region, -1, -1);
990 DrawBlack( p_line, i_width, p_region, -1, 1);
991 DrawBlack( p_line, i_width, p_region, 1, -1);
992 DrawBlack( p_line, i_width, p_region, 1, 1);
994 DrawBlack( p_line, i_width, p_region, -2, 0);
995 DrawBlack( p_line, i_width, p_region, 0, -2);
996 DrawBlack( p_line, i_width, p_region, 2, 0);
997 DrawBlack( p_line, i_width, p_region, 0, 2);
999 DrawBlack( p_line, i_width, p_region, -2, -2);
1000 DrawBlack( p_line, i_width, p_region, -2, 2);
1001 DrawBlack( p_line, i_width, p_region, 2, -2);
1002 DrawBlack( p_line, i_width, p_region, 2, 2);
1004 DrawBlack( p_line, i_width, p_region, -3, 0);
1005 DrawBlack( p_line, i_width, p_region, 0, -3);
1006 DrawBlack( p_line, i_width, p_region, 3, 0);
1007 DrawBlack( p_line, i_width, p_region, 0, 3);
1010 for( ; p_line != NULL; p_line = p_line->p_next )
1012 int i_glyph_tmax = 0;
1013 int i_bitmap_offset, i_offset, i_align_offset = 0;
1014 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1016 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1017 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
1020 if( p_line->i_width < i_width )
1022 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1024 i_align_offset = i_width - p_line->i_width;
1026 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1028 i_align_offset = ( i_width - p_line->i_width ) / 2;
1032 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1034 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1036 i_offset = ( p_line->p_glyph_pos[ i ].y +
1037 i_glyph_tmax - p_glyph->top + 3 ) *
1038 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1041 if( p_line->b_new_color_mode )
1043 /* Every glyph can (and in fact must) have its own color */
1044 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1047 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1049 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1051 uint8_t i_y_local = i_y;
1052 uint8_t i_u_local = i_u;
1053 uint8_t i_v_local = i_v;
1055 if( p_line->p_fg_bg_ratio != 0x00 )
1057 int i_split = p_glyph->bitmap.width *
1058 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1062 YUVFromRGB( p_line->p_bg_rgb[ i ],
1063 &i_y_local, &i_u_local, &i_v_local );
1067 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1069 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1070 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1072 p_dst_u[i_offset+x] = i_u;
1073 p_dst_v[i_offset+x] = i_v;
1075 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1076 p_dst_a[i_offset+x] = 0xff;
1079 i_offset += i_pitch;
1082 if( p_line->pi_underline_thickness[ i ] )
1084 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1085 p_line->pi_underline_offset[ i ],
1086 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1087 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1088 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1089 i_glyph_tmax, i_align_offset,
1096 /* Apply the alpha setting */
1097 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1098 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1104 * This function renders a text subpicture region into another one.
1105 * It also calculates the size needed for this string, and renders the
1106 * needed glyphs into memory. It is used as pf_add_string callback in
1107 * the vout method by this module
1109 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1110 subpicture_region_t *p_region_in )
1112 filter_sys_t *p_sys = p_filter->p_sys;
1113 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1114 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1115 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1116 int i_string_length;
1118 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1119 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1129 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1130 psz_string = p_region_in->psz_text;
1131 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1133 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1134 i_scale = val.i_int;
1136 if( p_region_in->p_style )
1138 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1139 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1140 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1144 i_font_color = p_sys->i_font_color;
1145 i_font_alpha = 255 - p_sys->i_font_opacity;
1146 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1149 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1150 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1151 SetFontSize( p_filter, i_font_size );
1153 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1154 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1155 i_blue = i_font_color & 0x000000FF;
1157 result.x = result.y = 0;
1158 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1160 psz_unicode = psz_unicode_orig =
1161 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1162 if( psz_unicode == NULL )
1164 #if defined(WORDS_BIGENDIAN)
1165 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1167 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1169 if( iconv_handle == (vlc_iconv_t)-1 )
1171 msg_Warn( p_filter, "unable to do conversion" );
1177 const char *p_in_buffer = psz_string;
1178 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1179 i_in_bytes = strlen( psz_string );
1180 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1181 i_out_bytes_left = i_out_bytes;
1182 p_out_buffer = (char *)psz_unicode;
1183 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1185 &p_out_buffer, &i_out_bytes_left );
1187 vlc_iconv_close( iconv_handle );
1191 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1192 "bytes left %u", (unsigned)i_in_bytes );
1195 *(uint32_t*)p_out_buffer = 0;
1196 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1199 #if defined(HAVE_FRIBIDI)
1201 uint32_t *p_fribidi_string;
1202 int32_t start_pos, pos = 0;
1204 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1205 if( !p_fribidi_string )
1208 /* Do bidi conversion line-by-line */
1209 while( pos < i_string_length )
1211 while( pos < i_string_length )
1213 i_char = psz_unicode[pos];
1214 if (i_char != '\r' && i_char != '\n')
1216 p_fribidi_string[pos] = i_char;
1220 while( pos < i_string_length )
1222 i_char = psz_unicode[pos];
1223 if (i_char == '\r' || i_char == '\n')
1227 if (pos > start_pos)
1229 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1230 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1233 (FriBidiChar*)p_fribidi_string + start_pos,
1238 free( psz_unicode_orig );
1239 psz_unicode = psz_unicode_orig = p_fribidi_string;
1240 p_fribidi_string[ i_string_length ] = 0;
1244 /* Calculate relative glyph positions and a bounding box for the
1246 if( !(p_line = NewLine( strlen( psz_string ))) )
1249 i_pen_x = i_pen_y = 0;
1251 psz_line_start = psz_unicode;
1253 #define face p_sys->p_face
1254 #define glyph face->glyph
1256 while( *psz_unicode )
1258 i_char = *psz_unicode++;
1259 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1264 if( i_char == '\n' )
1266 psz_line_start = psz_unicode;
1267 if( !(p_next = NewLine( strlen( psz_string ))) )
1269 p_line->p_next = p_next;
1270 p_line->i_width = line.xMax;
1271 p_line->i_height = face->size->metrics.height >> 6;
1272 p_line->pp_glyphs[ i ] = NULL;
1273 p_line->i_alpha = i_font_alpha;
1274 p_line->i_red = i_red;
1275 p_line->i_green = i_green;
1276 p_line->i_blue = i_blue;
1279 result.x = __MAX( result.x, line.xMax );
1280 result.y += face->size->metrics.height >> 6;
1283 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1284 i_pen_y += face->size->metrics.height >> 6;
1286 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1291 i_glyph_index = FT_Get_Char_Index( face, i_char );
1292 if( p_sys->i_use_kerning && i_glyph_index
1296 FT_Get_Kerning( face, i_previous, i_glyph_index,
1297 ft_kerning_default, &delta );
1298 i_pen_x += delta.x >> 6;
1301 p_line->p_glyph_pos[ i ].x = i_pen_x;
1302 p_line->p_glyph_pos[ i ].y = i_pen_y;
1303 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1306 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1310 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1313 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1317 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1318 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1321 FT_Done_Glyph( tmp_glyph );
1324 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1327 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1328 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1329 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1331 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1332 p_line->pp_glyphs[ i ] = NULL;
1334 p_line = NewLine( strlen( psz_string ));
1335 if( p_prev ) p_prev->p_next = p_line;
1336 else p_lines = p_line;
1338 uint32_t *psz_unicode_saved = psz_unicode;
1339 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1343 if( psz_unicode == psz_line_start )
1344 { /* try harder to break that line */
1345 psz_unicode = psz_unicode_saved;
1346 while( psz_unicode > psz_line_start &&
1347 *psz_unicode != '_' && *psz_unicode != '/' &&
1348 *psz_unicode != '\\' && *psz_unicode != '.' )
1353 if( psz_unicode == psz_line_start )
1355 msg_Warn( p_filter, "unbreakable string" );
1360 *psz_unicode = '\n';
1362 psz_unicode = psz_line_start;
1365 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1368 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1369 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1371 i_previous = i_glyph_index;
1372 i_pen_x += glyph->advance.x >> 6;
1376 p_line->i_width = line.xMax;
1377 p_line->i_height = face->size->metrics.height >> 6;
1378 p_line->pp_glyphs[ i ] = NULL;
1379 p_line->i_alpha = i_font_alpha;
1380 p_line->i_red = i_red;
1381 p_line->i_green = i_green;
1382 p_line->i_blue = i_blue;
1383 result.x = __MAX( result.x, line.xMax );
1384 result.y += line.yMax - line.yMin;
1389 p_region_out->i_x = p_region_in->i_x;
1390 p_region_out->i_y = p_region_in->i_y;
1392 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1393 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1395 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1397 free( psz_unicode_orig );
1398 FreeLines( p_lines );
1402 free( psz_unicode_orig );
1403 FreeLines( p_lines );
1404 return VLC_EGENERIC;
1407 #ifdef HAVE_FONTCONFIG
1408 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1409 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1410 bool b_italic, bool b_uline )
1412 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1416 p_style->i_font_size = i_font_size;
1417 p_style->i_font_color = i_font_color;
1418 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1419 p_style->b_italic = b_italic;
1420 p_style->b_bold = b_bold;
1421 p_style->b_underline = b_uline;
1423 p_style->psz_fontname = strdup( psz_fontname );
1428 static void DeleteStyle( ft_style_t *p_style )
1432 free( p_style->psz_fontname );
1437 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1444 if(( s1->i_font_size == s2->i_font_size ) &&
1445 ( s1->i_font_color == s2->i_font_color ) &&
1446 ( s1->b_italic == s2->b_italic ) &&
1447 ( s1->b_bold == s2->b_bold ) &&
1448 ( s1->b_underline == s2->b_underline ) &&
1449 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1456 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1457 uint32_t i_color, uint32_t i_karaoke_bg_color )
1459 font_stack_t *p_new;
1462 return VLC_EGENERIC;
1464 p_new = malloc( sizeof( font_stack_t ) );
1468 p_new->p_next = NULL;
1471 p_new->psz_name = strdup( psz_name );
1473 p_new->psz_name = NULL;
1475 p_new->i_size = i_size;
1476 p_new->i_color = i_color;
1477 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1485 font_stack_t *p_last;
1487 for( p_last = *p_font;
1489 p_last = p_last->p_next )
1492 p_last->p_next = p_new;
1497 static int PopFont( font_stack_t **p_font )
1499 font_stack_t *p_last, *p_next_to_last;
1501 if( !p_font || !*p_font )
1502 return VLC_EGENERIC;
1504 p_next_to_last = NULL;
1505 for( p_last = *p_font;
1507 p_last = p_last->p_next )
1509 p_next_to_last = p_last;
1512 if( p_next_to_last )
1513 p_next_to_last->p_next = NULL;
1517 free( p_last->psz_name );
1523 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1524 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1526 font_stack_t *p_last;
1528 if( !p_font || !*p_font )
1529 return VLC_EGENERIC;
1531 for( p_last=*p_font;
1533 p_last=p_last->p_next )
1536 *psz_name = p_last->psz_name;
1537 *i_size = p_last->i_size;
1538 *i_color = p_last->i_color;
1539 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1544 static void IconvText( filter_t *p_filter, const char *psz_string,
1545 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1547 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1549 /* If memory hasn't been allocated for our output string, allocate it here
1550 * - the calling function must now be responsible for freeing it.
1552 if( !*ppsz_unicode )
1553 *ppsz_unicode = (uint32_t *)
1554 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1556 /* We don't need to handle a NULL pointer in *ppsz_unicode
1557 * if we are instead testing for a non NULL value like we are here */
1561 #if defined(WORDS_BIGENDIAN)
1562 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1564 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1566 if( iconv_handle != (vlc_iconv_t)-1 )
1568 char *p_in_buffer, *p_out_buffer;
1569 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1570 i_in_bytes = strlen( psz_string );
1571 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1572 i_out_bytes_left = i_out_bytes;
1573 p_in_buffer = (char *) psz_string;
1574 p_out_buffer = (char *) *ppsz_unicode;
1575 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1576 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1578 vlc_iconv_close( iconv_handle );
1582 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1583 "bytes left %u", (unsigned)i_in_bytes );
1587 *(uint32_t*)p_out_buffer = 0;
1589 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1594 msg_Warn( p_filter, "unable to do conversion" );
1599 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1600 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1603 ft_style_t *p_style = NULL;
1605 char *psz_fontname = NULL;
1606 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1607 uint32_t i_karaoke_bg_color = i_font_color;
1608 int i_font_size = p_sys->i_font_size;
1610 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1611 &i_font_color, &i_karaoke_bg_color ))
1613 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1614 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1619 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1620 bool b_uline, int i_karaoke_bgcolor,
1621 line_desc_t *p_line, uint32_t *psz_unicode,
1622 int *pi_pen_x, int i_pen_y, int *pi_start,
1623 FT_Vector *p_result )
1628 bool b_first_on_line = true;
1631 int i_pen_x_start = *pi_pen_x;
1633 uint32_t *psz_unicode_start = psz_unicode;
1635 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1637 /* Account for part of line already in position */
1638 for( i=0; i<*pi_start; i++ )
1642 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1643 ft_glyph_bbox_pixels, &glyph_size );
1645 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1646 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1647 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1648 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1654 b_first_on_line = false;
1656 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1662 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1663 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1667 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1668 ft_kerning_default, &delta );
1669 *pi_pen_x += delta.x >> 6;
1671 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1672 p_line->p_glyph_pos[ i ].y = i_pen_y;
1674 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1678 "unable to render text FT_Load_Glyph returned %d", i_error );
1679 p_line->pp_glyphs[ i ] = NULL;
1680 return VLC_EGENERIC;
1682 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1686 "unable to render text FT_Get_Glyph returned %d", i_error );
1687 p_line->pp_glyphs[ i ] = NULL;
1688 return VLC_EGENERIC;
1690 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1691 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1694 FT_Done_Glyph( tmp_glyph );
1699 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1700 p_face->size->metrics.y_scale));
1701 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1702 p_face->size->metrics.y_scale));
1704 p_line->pi_underline_offset[ i ] =
1705 ( aOffset < 0 ) ? -aOffset : aOffset;
1706 p_line->pi_underline_thickness[ i ] =
1707 ( aSize < 0 ) ? -aSize : aSize;
1709 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1710 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1711 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1712 p_line->p_fg_bg_ratio[ i ] = 0x00;
1714 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1715 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1716 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1718 for( ; i >= *pi_start; i-- )
1719 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1722 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1726 if( psz_unicode == psz_unicode_start )
1728 if( b_first_on_line )
1730 msg_Warn( p_filter, "unbreakable string" );
1731 p_line->pp_glyphs[ i ] = NULL;
1732 return VLC_EGENERIC;
1734 *pi_pen_x = i_pen_x_start;
1736 p_line->i_width = line.xMax;
1737 p_line->i_height = __MAX( p_line->i_height,
1738 p_face->size->metrics.height >> 6 );
1739 p_line->pp_glyphs[ i ] = NULL;
1741 p_result->x = __MAX( p_result->x, line.xMax );
1742 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1743 i_yMax - i_yMin ) );
1748 *psz_unicode = '\n';
1750 psz_unicode = psz_unicode_start;
1751 *pi_pen_x = i_pen_x_start;
1759 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1760 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1762 i_previous = i_glyph_index;
1763 *pi_pen_x += p_face->glyph->advance.x >> 6;
1766 p_line->i_width = line.xMax;
1767 p_line->i_height = __MAX( p_line->i_height,
1768 p_face->size->metrics.height >> 6 );
1769 p_line->pp_glyphs[ i ] = NULL;
1771 p_result->x = __MAX( p_result->x, line.xMax );
1772 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1773 line.yMax - line.yMin ) );
1777 /* Get rid of any text processed - if necessary repositioning
1778 * at the start of a new line of text
1782 *psz_unicode_start = '\0';
1784 else if( psz_unicode > psz_unicode_start )
1786 for( i=0; psz_unicode[ i ]; i++ )
1787 psz_unicode_start[ i ] = psz_unicode[ i ];
1788 psz_unicode_start[ i ] = '\0';
1794 static const struct {
1795 const char *psz_name;
1797 } p_html_colors[] = {
1798 /* Official html colors */
1799 { "Aqua", 0x00FFFF },
1800 { "Black", 0x000000 },
1801 { "Blue", 0x0000FF },
1802 { "Fuchsia", 0xFF00FF },
1803 { "Gray", 0x808080 },
1804 { "Green", 0x008000 },
1805 { "Lime", 0x00FF00 },
1806 { "Maroon", 0x800000 },
1807 { "Navy", 0x000080 },
1808 { "Olive", 0x808000 },
1809 { "Purple", 0x800080 },
1810 { "Red", 0xFF0000 },
1811 { "Silver", 0xC0C0C0 },
1812 { "Teal", 0x008080 },
1813 { "White", 0xFFFFFF },
1814 { "Yellow", 0xFFFF00 },
1817 { "AliceBlue", 0xF0F8FF },
1818 { "AntiqueWhite", 0xFAEBD7 },
1819 { "Aqua", 0x00FFFF },
1820 { "Aquamarine", 0x7FFFD4 },
1821 { "Azure", 0xF0FFFF },
1822 { "Beige", 0xF5F5DC },
1823 { "Bisque", 0xFFE4C4 },
1824 { "Black", 0x000000 },
1825 { "BlanchedAlmond", 0xFFEBCD },
1826 { "Blue", 0x0000FF },
1827 { "BlueViolet", 0x8A2BE2 },
1828 { "Brown", 0xA52A2A },
1829 { "BurlyWood", 0xDEB887 },
1830 { "CadetBlue", 0x5F9EA0 },
1831 { "Chartreuse", 0x7FFF00 },
1832 { "Chocolate", 0xD2691E },
1833 { "Coral", 0xFF7F50 },
1834 { "CornflowerBlue", 0x6495ED },
1835 { "Cornsilk", 0xFFF8DC },
1836 { "Crimson", 0xDC143C },
1837 { "Cyan", 0x00FFFF },
1838 { "DarkBlue", 0x00008B },
1839 { "DarkCyan", 0x008B8B },
1840 { "DarkGoldenRod", 0xB8860B },
1841 { "DarkGray", 0xA9A9A9 },
1842 { "DarkGrey", 0xA9A9A9 },
1843 { "DarkGreen", 0x006400 },
1844 { "DarkKhaki", 0xBDB76B },
1845 { "DarkMagenta", 0x8B008B },
1846 { "DarkOliveGreen", 0x556B2F },
1847 { "Darkorange", 0xFF8C00 },
1848 { "DarkOrchid", 0x9932CC },
1849 { "DarkRed", 0x8B0000 },
1850 { "DarkSalmon", 0xE9967A },
1851 { "DarkSeaGreen", 0x8FBC8F },
1852 { "DarkSlateBlue", 0x483D8B },
1853 { "DarkSlateGray", 0x2F4F4F },
1854 { "DarkSlateGrey", 0x2F4F4F },
1855 { "DarkTurquoise", 0x00CED1 },
1856 { "DarkViolet", 0x9400D3 },
1857 { "DeepPink", 0xFF1493 },
1858 { "DeepSkyBlue", 0x00BFFF },
1859 { "DimGray", 0x696969 },
1860 { "DimGrey", 0x696969 },
1861 { "DodgerBlue", 0x1E90FF },
1862 { "FireBrick", 0xB22222 },
1863 { "FloralWhite", 0xFFFAF0 },
1864 { "ForestGreen", 0x228B22 },
1865 { "Fuchsia", 0xFF00FF },
1866 { "Gainsboro", 0xDCDCDC },
1867 { "GhostWhite", 0xF8F8FF },
1868 { "Gold", 0xFFD700 },
1869 { "GoldenRod", 0xDAA520 },
1870 { "Gray", 0x808080 },
1871 { "Grey", 0x808080 },
1872 { "Green", 0x008000 },
1873 { "GreenYellow", 0xADFF2F },
1874 { "HoneyDew", 0xF0FFF0 },
1875 { "HotPink", 0xFF69B4 },
1876 { "IndianRed", 0xCD5C5C },
1877 { "Indigo", 0x4B0082 },
1878 { "Ivory", 0xFFFFF0 },
1879 { "Khaki", 0xF0E68C },
1880 { "Lavender", 0xE6E6FA },
1881 { "LavenderBlush", 0xFFF0F5 },
1882 { "LawnGreen", 0x7CFC00 },
1883 { "LemonChiffon", 0xFFFACD },
1884 { "LightBlue", 0xADD8E6 },
1885 { "LightCoral", 0xF08080 },
1886 { "LightCyan", 0xE0FFFF },
1887 { "LightGoldenRodYellow", 0xFAFAD2 },
1888 { "LightGray", 0xD3D3D3 },
1889 { "LightGrey", 0xD3D3D3 },
1890 { "LightGreen", 0x90EE90 },
1891 { "LightPink", 0xFFB6C1 },
1892 { "LightSalmon", 0xFFA07A },
1893 { "LightSeaGreen", 0x20B2AA },
1894 { "LightSkyBlue", 0x87CEFA },
1895 { "LightSlateGray", 0x778899 },
1896 { "LightSlateGrey", 0x778899 },
1897 { "LightSteelBlue", 0xB0C4DE },
1898 { "LightYellow", 0xFFFFE0 },
1899 { "Lime", 0x00FF00 },
1900 { "LimeGreen", 0x32CD32 },
1901 { "Linen", 0xFAF0E6 },
1902 { "Magenta", 0xFF00FF },
1903 { "Maroon", 0x800000 },
1904 { "MediumAquaMarine", 0x66CDAA },
1905 { "MediumBlue", 0x0000CD },
1906 { "MediumOrchid", 0xBA55D3 },
1907 { "MediumPurple", 0x9370D8 },
1908 { "MediumSeaGreen", 0x3CB371 },
1909 { "MediumSlateBlue", 0x7B68EE },
1910 { "MediumSpringGreen", 0x00FA9A },
1911 { "MediumTurquoise", 0x48D1CC },
1912 { "MediumVioletRed", 0xC71585 },
1913 { "MidnightBlue", 0x191970 },
1914 { "MintCream", 0xF5FFFA },
1915 { "MistyRose", 0xFFE4E1 },
1916 { "Moccasin", 0xFFE4B5 },
1917 { "NavajoWhite", 0xFFDEAD },
1918 { "Navy", 0x000080 },
1919 { "OldLace", 0xFDF5E6 },
1920 { "Olive", 0x808000 },
1921 { "OliveDrab", 0x6B8E23 },
1922 { "Orange", 0xFFA500 },
1923 { "OrangeRed", 0xFF4500 },
1924 { "Orchid", 0xDA70D6 },
1925 { "PaleGoldenRod", 0xEEE8AA },
1926 { "PaleGreen", 0x98FB98 },
1927 { "PaleTurquoise", 0xAFEEEE },
1928 { "PaleVioletRed", 0xD87093 },
1929 { "PapayaWhip", 0xFFEFD5 },
1930 { "PeachPuff", 0xFFDAB9 },
1931 { "Peru", 0xCD853F },
1932 { "Pink", 0xFFC0CB },
1933 { "Plum", 0xDDA0DD },
1934 { "PowderBlue", 0xB0E0E6 },
1935 { "Purple", 0x800080 },
1936 { "Red", 0xFF0000 },
1937 { "RosyBrown", 0xBC8F8F },
1938 { "RoyalBlue", 0x4169E1 },
1939 { "SaddleBrown", 0x8B4513 },
1940 { "Salmon", 0xFA8072 },
1941 { "SandyBrown", 0xF4A460 },
1942 { "SeaGreen", 0x2E8B57 },
1943 { "SeaShell", 0xFFF5EE },
1944 { "Sienna", 0xA0522D },
1945 { "Silver", 0xC0C0C0 },
1946 { "SkyBlue", 0x87CEEB },
1947 { "SlateBlue", 0x6A5ACD },
1948 { "SlateGray", 0x708090 },
1949 { "SlateGrey", 0x708090 },
1950 { "Snow", 0xFFFAFA },
1951 { "SpringGreen", 0x00FF7F },
1952 { "SteelBlue", 0x4682B4 },
1953 { "Tan", 0xD2B48C },
1954 { "Teal", 0x008080 },
1955 { "Thistle", 0xD8BFD8 },
1956 { "Tomato", 0xFF6347 },
1957 { "Turquoise", 0x40E0D0 },
1958 { "Violet", 0xEE82EE },
1959 { "Wheat", 0xF5DEB3 },
1960 { "White", 0xFFFFFF },
1961 { "WhiteSmoke", 0xF5F5F5 },
1962 { "Yellow", 0xFFFF00 },
1963 { "YellowGreen", 0x9ACD32 },
1968 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1969 font_stack_t **p_fonts, int i_scale )
1972 char *psz_fontname = NULL;
1973 uint32_t i_font_color = 0xffffff;
1974 int i_font_alpha = 0;
1975 uint32_t i_karaoke_bg_color = 0x00ffffff;
1976 int i_font_size = 24;
1978 /* Default all attributes to the top font in the stack -- in case not
1979 * all attributes are specified in the sub-font
1981 if( VLC_SUCCESS == PeekFont( p_fonts,
1985 &i_karaoke_bg_color ))
1987 psz_fontname = strdup( psz_fontname );
1988 i_font_size = i_font_size * 1000 / i_scale;
1990 i_font_alpha = (i_font_color >> 24) & 0xff;
1991 i_font_color &= 0x00ffffff;
1993 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1995 char *psz_name = xml_ReaderName( p_xml_reader );
1996 char *psz_value = xml_ReaderValue( p_xml_reader );
1998 if( psz_name && psz_value )
2000 if( !strcasecmp( "face", psz_name ) )
2002 free( psz_fontname );
2003 psz_fontname = strdup( psz_value );
2005 else if( !strcasecmp( "size", psz_name ) )
2007 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
2009 int i_value = atoi( psz_value );
2011 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
2012 i_font_size += ( i_value * i_font_size ) / 10;
2013 else if( i_value < -5 )
2014 i_font_size = - i_value;
2015 else if( i_value > 5 )
2016 i_font_size = i_value;
2019 i_font_size = atoi( psz_value );
2021 else if( !strcasecmp( "color", psz_name ) )
2023 if( psz_value[0] == '#' )
2025 i_font_color = strtol( psz_value + 1, NULL, 16 );
2026 i_font_color &= 0x00ffffff;
2030 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
2032 if( !strncasecmp( psz_value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
2034 i_font_color = p_html_colors[i].i_value;
2040 else if( !strcasecmp( "alpha", psz_name ) &&
2041 ( psz_value[0] == '#' ) )
2043 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
2044 i_font_alpha &= 0xff;
2050 rv = PushFont( p_fonts,
2052 i_font_size * i_scale / 1000,
2053 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
2054 i_karaoke_bg_color );
2056 free( psz_fontname );
2061 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2062 uint32_t **psz_text_out, uint32_t *pi_runs,
2063 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2064 ft_style_t *p_style )
2066 uint32_t i_string_length = 0;
2068 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
2069 *psz_text_out += i_string_length;
2071 if( ppp_styles && ppi_run_lengths )
2077 *ppp_styles = (ft_style_t **)
2078 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
2080 else if( *pi_runs == 1 )
2082 *ppp_styles = (ft_style_t **)
2083 malloc( *pi_runs * sizeof( ft_style_t * ) );
2086 /* We have just malloc'ed this memory successfully -
2087 * *pi_runs HAS to be within the memory area of *ppp_styles */
2090 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
2094 if( *ppi_run_lengths )
2096 *ppi_run_lengths = (uint32_t *)
2097 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
2099 else if( *pi_runs == 1 )
2101 *ppi_run_lengths = (uint32_t *)
2102 malloc( *pi_runs * sizeof( uint32_t ) );
2105 /* same remarks here */
2106 if( *ppi_run_lengths )
2108 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
2111 /* If we couldn't use the p_style argument due to memory allocation
2112 * problems above, release it here.
2114 if( p_style ) DeleteStyle( p_style );
2117 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
2118 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
2120 /* Karaoke tags _PRECEDE_ the text they specify a duration
2121 * for, therefore we are working out the length for the
2122 * previous tag, and first time through we have nothing
2124 if( pi_k_run_lengths )
2129 /* Work out how many characters are presently in the string
2131 for( i = 0; i < i_runs; i++ )
2132 i_chars += pi_run_lengths[ i ];
2134 /* Subtract away those we've already allocated to other
2137 for( i = 0; i < i_k_runs; i++ )
2138 i_chars -= pi_k_run_lengths[ i ];
2140 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
2144 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
2145 uint32_t **ppi_k_run_lengths,
2146 uint32_t **ppi_k_durations )
2148 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
2150 char *psz_name = xml_ReaderName( p_xml_reader );
2151 char *psz_value = xml_ReaderValue( p_xml_reader );
2153 if( psz_name && psz_value &&
2154 !strcasecmp( "t", psz_name ) )
2156 if( ppi_k_durations && ppi_k_run_lengths )
2160 if( *ppi_k_durations )
2162 *ppi_k_durations = (uint32_t *)
2163 realloc( *ppi_k_durations,
2164 *pi_k_runs * sizeof( uint32_t ) );
2166 else if( *pi_k_runs == 1 )
2168 *ppi_k_durations = (uint32_t *)
2169 malloc( *pi_k_runs * sizeof( uint32_t ) );
2172 if( *ppi_k_run_lengths )
2174 *ppi_k_run_lengths = (uint32_t *)
2175 realloc( *ppi_k_run_lengths,
2176 *pi_k_runs * sizeof( uint32_t ) );
2178 else if( *pi_k_runs == 1 )
2180 *ppi_k_run_lengths = (uint32_t *)
2181 malloc( *pi_k_runs * sizeof( uint32_t ) );
2183 if( *ppi_k_durations )
2184 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
2186 if( *ppi_k_run_lengths )
2187 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
2195 static int ProcessNodes( filter_t *p_filter,
2196 xml_reader_t *p_xml_reader,
2197 text_style_t *p_font_style,
2202 uint32_t **ppi_run_lengths,
2203 ft_style_t ***ppp_styles,
2206 uint32_t *pi_k_runs,
2207 uint32_t **ppi_k_run_lengths,
2208 uint32_t **ppi_k_durations )
2210 int rv = VLC_SUCCESS;
2211 filter_sys_t *p_sys = p_filter->p_sys;
2212 uint32_t *psz_text_orig = psz_text;
2213 font_stack_t *p_fonts = NULL;
2217 char *psz_node = NULL;
2219 bool b_italic = false;
2220 bool b_bold = false;
2221 bool b_uline = false;
2223 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2224 i_scale = val.i_int;
2228 rv = PushFont( &p_fonts,
2229 p_font_style->psz_fontname,
2230 p_font_style->i_font_size * i_scale / 1000,
2231 (p_font_style->i_font_color & 0xffffff) |
2232 ((p_font_style->i_font_alpha & 0xff) << 24),
2233 (p_font_style->i_karaoke_background_color & 0xffffff) |
2234 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2236 if( p_font_style->i_style_flags & STYLE_BOLD )
2238 if( p_font_style->i_style_flags & STYLE_ITALIC )
2240 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2245 rv = PushFont( &p_fonts,
2251 if( rv != VLC_SUCCESS )
2254 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2256 switch ( xml_ReaderNodeType( p_xml_reader ) )
2258 case XML_READER_NONE:
2260 case XML_READER_ENDELEM:
2261 psz_node = xml_ReaderName( p_xml_reader );
2265 if( !strcasecmp( "font", psz_node ) )
2266 PopFont( &p_fonts );
2267 else if( !strcasecmp( "b", psz_node ) )
2269 else if( !strcasecmp( "i", psz_node ) )
2271 else if( !strcasecmp( "u", psz_node ) )
2277 case XML_READER_STARTELEM:
2278 psz_node = xml_ReaderName( p_xml_reader );
2281 if( !strcasecmp( "font", psz_node ) )
2282 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2283 else if( !strcasecmp( "b", psz_node ) )
2285 else if( !strcasecmp( "i", psz_node ) )
2287 else if( !strcasecmp( "u", psz_node ) )
2289 else if( !strcasecmp( "br", psz_node ) )
2291 SetupLine( p_filter, "\n", &psz_text,
2292 pi_runs, ppi_run_lengths, ppp_styles,
2293 GetStyleFromFontStack( p_sys,
2299 else if( !strcasecmp( "k", psz_node ) )
2301 /* Only valid in karaoke */
2304 if( *pi_k_runs > 0 )
2306 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2307 *pi_k_runs, *ppi_k_run_lengths );
2309 SetupKaraoke( p_xml_reader, pi_k_runs,
2310 ppi_k_run_lengths, ppi_k_durations );
2317 case XML_READER_TEXT:
2318 psz_node = xml_ReaderValue( p_xml_reader );
2321 /* Turn any multiple-whitespaces into single spaces */
2322 char *s = strpbrk( psz_node, "\t\r\n " );
2325 int i_whitespace = strspn( s, "\t\r\n " );
2327 if( i_whitespace > 1 )
2330 strlen( s ) - i_whitespace + 1 );
2333 s = strpbrk( s, "\t\r\n " );
2335 SetupLine( p_filter, psz_node, &psz_text,
2336 pi_runs, ppi_run_lengths, ppp_styles,
2337 GetStyleFromFontStack( p_sys,
2346 if( rv != VLC_SUCCESS )
2348 psz_text = psz_text_orig;
2354 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2355 *pi_k_runs, *ppi_k_run_lengths );
2358 *pi_len = psz_text - psz_text_orig;
2360 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2365 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2369 for( k=0; k < p_sys->i_font_attachments; k++ )
2371 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2373 FT_Face p_face = NULL;
2375 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2383 bool match = !strcasecmp( p_face->family_name,
2384 p_style->psz_fontname );
2386 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2387 match = match && p_style->b_bold;
2389 match = match && !p_style->b_bold;
2391 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2392 match = match && p_style->b_italic;
2394 match = match && !p_style->b_italic;
2402 FT_Done_Face( p_face );
2407 return VLC_EGENERIC;
2410 static int ProcessLines( filter_t *p_filter,
2415 uint32_t *pi_run_lengths,
2416 ft_style_t **pp_styles,
2417 line_desc_t **pp_lines,
2419 FT_Vector *p_result,
2423 uint32_t *pi_k_run_lengths,
2424 uint32_t *pi_k_durations )
2426 filter_sys_t *p_sys = p_filter->p_sys;
2427 ft_style_t **pp_char_styles;
2428 int *p_new_positions = NULL;
2429 int8_t *p_levels = NULL;
2430 uint8_t *pi_karaoke_bar = NULL;
2434 /* Assign each character in the text string its style explicitly, so that
2435 * after the characters have been shuffled around by Fribidi, we can re-apply
2436 * the styles, and to simplify the calculation of runs within a line.
2438 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2439 if( !pp_char_styles )
2444 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2445 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2446 * we just won't be able to display the progress bar; at least we'll
2452 for( j = 0; j < i_runs; j++ )
2453 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2454 pp_char_styles[ i++ ] = pp_styles[ j ];
2456 #if defined(HAVE_FRIBIDI)
2458 ft_style_t **pp_char_styles_new;
2459 int *p_old_positions;
2460 uint32_t *p_fribidi_string;
2461 int start_pos, pos = 0;
2463 pp_char_styles_new = (ft_style_t **)
2464 malloc( i_len * sizeof( ft_style_t * ));
2466 p_fribidi_string = (uint32_t *)
2467 malloc( (i_len + 1) * sizeof(uint32_t) );
2468 p_old_positions = (int *)
2469 malloc( (i_len + 1) * sizeof( int ) );
2470 p_new_positions = (int *)
2471 malloc( (i_len + 1) * sizeof( int ) );
2472 p_levels = (int8_t *)
2473 malloc( (i_len + 1) * sizeof( int8_t ) );
2475 if( ! pp_char_styles_new ||
2476 ! p_fribidi_string ||
2477 ! p_old_positions ||
2478 ! p_new_positions ||
2482 free( p_old_positions );
2483 free( p_new_positions );
2484 free( p_fribidi_string );
2485 free( pp_char_styles_new );
2486 free( pi_karaoke_bar );
2488 free( pp_char_styles );
2492 /* Do bidi conversion line-by-line */
2495 while(pos < i_len) {
2496 if (psz_text[pos] != '\n')
2498 p_fribidi_string[pos] = psz_text[pos];
2499 pp_char_styles_new[pos] = pp_char_styles[pos];
2500 p_new_positions[pos] = pos;
2505 while(pos < i_len) {
2506 if (psz_text[pos] == '\n')
2510 if (pos > start_pos)
2512 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2513 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2514 pos - start_pos, &base_dir,
2515 (FriBidiChar*)p_fribidi_string + start_pos,
2516 p_new_positions + start_pos,
2518 p_levels + start_pos );
2519 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2521 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2522 p_old_positions[ j - start_pos ] ];
2523 p_new_positions[ j ] += start_pos;
2527 free( p_old_positions );
2528 free( pp_char_styles );
2529 pp_char_styles = pp_char_styles_new;
2530 psz_text = p_fribidi_string;
2531 p_fribidi_string[ i_len ] = 0;
2534 /* Work out the karaoke */
2535 if( pi_karaoke_bar )
2537 int64_t i_last_duration = 0;
2538 int64_t i_duration = 0;
2539 int64_t i_start_pos = 0;
2540 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2542 for( k = 0; k< i_k_runs; k++ )
2544 double fraction = 0.0;
2546 i_duration += pi_k_durations[ k ];
2548 if( i_duration < i_elapsed )
2550 /* Completely finished this run-length -
2551 * let it render normally */
2555 else if( i_elapsed < i_last_duration )
2557 /* Haven't got up to this segment yet -
2558 * render it completely in karaoke BG mode */
2564 /* Partway through this run */
2566 fraction = (double)(i_elapsed - i_last_duration) /
2567 (double)pi_k_durations[ k ];
2569 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2571 double shade = pi_k_run_lengths[ k ] * fraction;
2573 if( p_new_positions )
2574 j = p_new_positions[ i_start_pos + i ];
2576 j = i_start_pos + i;
2578 if( i < (uint32_t)shade )
2579 pi_karaoke_bar[ j ] = 0xff;
2580 else if( (double)i > shade )
2581 pi_karaoke_bar[ j ] = 0x00;
2584 shade -= (int)shade;
2585 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2586 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2590 i_last_duration = i_duration;
2591 i_start_pos += pi_k_run_lengths[ k ];
2595 free( p_new_positions );
2597 FT_Vector tmp_result;
2599 line_desc_t *p_line = NULL;
2600 line_desc_t *p_prev = NULL;
2606 p_result->x = p_result->y = 0;
2607 tmp_result.x = tmp_result.y = 0;
2610 for( k = 0; k <= (uint32_t) i_len; k++ )
2612 if( ( k == (uint32_t) i_len ) ||
2614 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2616 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2618 /* End of the current style run */
2619 FT_Face p_face = NULL;
2622 /* Look for a match amongst our attachments first */
2623 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2627 char *psz_fontfile = NULL;
2629 vlc_mutex_lock( p_sys->p_fontconfig_lock );
2630 if( p_sys->b_fontconfig_ok )
2632 /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
2633 * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
2634 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2635 p_style->psz_fontname,
2640 vlc_mutex_unlock( p_sys->p_fontconfig_lock );
2642 if( psz_fontfile && ! *psz_fontfile )
2644 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2645 " so using default font", p_style->psz_fontname,
2646 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2647 (p_style->b_bold ? "(Bold)" :
2648 (p_style->b_italic ? "(Italic)" : ""))) );
2649 free( psz_fontfile );
2650 psz_fontfile = NULL;
2655 if( FT_New_Face( p_sys->p_library,
2656 psz_fontfile, i_idx, &p_face ) )
2658 free( psz_fontfile );
2659 free( pp_char_styles );
2660 #if defined(HAVE_FRIBIDI)
2663 free( pi_karaoke_bar );
2664 return VLC_EGENERIC;
2666 free( psz_fontfile );
2670 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2672 /* We've loaded a font face which is unhelpful for actually
2673 * rendering text - fallback to the default one.
2675 FT_Done_Face( p_face );
2679 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2680 ft_encoding_unicode ) ||
2681 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2682 p_style->i_font_size ) )
2684 if( p_face ) FT_Done_Face( p_face );
2685 free( pp_char_styles );
2686 #if defined(HAVE_FRIBIDI)
2689 free( pi_karaoke_bar );
2690 return VLC_EGENERIC;
2692 p_sys->i_use_kerning =
2693 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2696 uint32_t *psz_unicode = (uint32_t *)
2697 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2700 if( p_face ) FT_Done_Face( p_face );
2701 free( pp_char_styles );
2702 free( psz_unicode );
2703 #if defined(HAVE_FRIBIDI)
2706 free( pi_karaoke_bar );
2709 memcpy( psz_unicode, psz_text + i_prev,
2710 sizeof( uint32_t ) * ( k - i_prev ) );
2711 psz_unicode[ k - i_prev ] = 0;
2712 while( *psz_unicode )
2716 if( !(p_line = NewLine( i_len - i_prev)) )
2718 if( p_face ) FT_Done_Face( p_face );
2719 free( pp_char_styles );
2720 free( psz_unicode );
2721 #if defined(HAVE_FRIBIDI)
2724 free( pi_karaoke_bar );
2727 /* New Color mode only works in YUVA rendering mode --
2728 * (RGB mode has palette constraints on it). We therefore
2729 * need to populate the legacy colour fields also.
2731 p_line->b_new_color_mode = true;
2732 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2733 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2734 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2735 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2736 p_line->p_next = NULL;
2738 i_pen_y += tmp_result.y;
2742 if( p_prev ) p_prev->p_next = p_line;
2743 else *pp_lines = p_line;
2746 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2747 p_style->i_font_color, p_style->b_underline,
2748 p_style->i_karaoke_bg_color,
2749 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2750 &tmp_result ) != VLC_SUCCESS )
2752 if( p_face ) FT_Done_Face( p_face );
2753 free( pp_char_styles );
2754 free( psz_unicode );
2755 #if defined(HAVE_FRIBIDI)
2758 free( pi_karaoke_bar );
2759 return VLC_EGENERIC;
2764 p_result->x = __MAX( p_result->x, tmp_result.x );
2765 p_result->y += tmp_result.y;
2770 if( *psz_unicode == '\n')
2774 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2776 *c_ptr = *(c_ptr+1);
2781 free( psz_unicode );
2782 if( p_face ) FT_Done_Face( p_face );
2786 free( pp_char_styles );
2787 #if defined(HAVE_FRIBIDI)
2792 p_result->x = __MAX( p_result->x, tmp_result.x );
2793 p_result->y += tmp_result.y;
2796 if( pi_karaoke_bar )
2799 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2801 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2803 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2807 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2809 /* 100% BG colour will render faster if we
2810 * instead make it 100% FG colour, so leave
2811 * the ratio alone and copy the value across
2813 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2817 if( pi_karaoke_bar[ i ] & 0x80 )
2819 /* Swap Left and Right sides over for Right aligned
2820 * language text (eg. Arabic, Hebrew)
2822 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2824 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2825 p_line->p_bg_rgb[ k ] = i_tmp;
2827 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2830 /* Jump over the '\n' at the line-end */
2833 free( pi_karaoke_bar );
2839 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2840 subpicture_region_t *p_region_in )
2842 int rv = VLC_SUCCESS;
2843 stream_t *p_sub = NULL;
2844 xml_t *p_xml = NULL;
2845 xml_reader_t *p_xml_reader = NULL;
2847 if( !p_region_in || !p_region_in->psz_html )
2848 return VLC_EGENERIC;
2850 /* Reset the default fontsize in case screen metrics have changed */
2851 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2853 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2854 (uint8_t *) p_region_in->psz_html,
2855 strlen( p_region_in->psz_html ),
2859 p_xml = xml_Create( p_filter );
2862 bool b_karaoke = false;
2864 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2867 /* Look for Root Node */
2868 if( xml_ReaderRead( p_xml_reader ) == 1 )
2870 char *psz_node = xml_ReaderName( p_xml_reader );
2872 if( !strcasecmp( "karaoke", psz_node ) )
2874 /* We're going to have to render the text a number
2875 * of times to show the progress marker on the text.
2877 var_SetBool( p_filter, "text-rerender", true );
2880 else if( !strcasecmp( "text", psz_node ) )
2886 /* Only text and karaoke tags are supported */
2887 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2888 xml_ReaderDelete( p_xml, p_xml_reader );
2889 p_xml_reader = NULL;
2901 uint32_t i_runs = 0;
2902 uint32_t i_k_runs = 0;
2903 uint32_t *pi_run_lengths = NULL;
2904 uint32_t *pi_k_run_lengths = NULL;
2905 uint32_t *pi_k_durations = NULL;
2906 ft_style_t **pp_styles = NULL;
2908 line_desc_t *p_lines = NULL;
2910 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2911 sizeof( uint32_t ) );
2916 rv = ProcessNodes( p_filter, p_xml_reader,
2917 p_region_in->p_style, psz_text, &i_len,
2918 &i_runs, &pi_run_lengths, &pp_styles,
2919 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2922 p_region_out->i_x = p_region_in->i_x;
2923 p_region_out->i_y = p_region_in->i_y;
2925 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2927 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2928 pi_run_lengths, pp_styles, &p_lines, &result,
2929 b_karaoke, i_k_runs, pi_k_run_lengths,
2933 for( k=0; k<i_runs; k++)
2934 DeleteStyle( pp_styles[k] );
2936 free( pi_run_lengths );
2939 /* Don't attempt to render text that couldn't be layed out
2942 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2944 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2946 Render( p_filter, p_region_out, p_lines,
2947 result.x, result.y );
2951 RenderYUVA( p_filter, p_region_out, p_lines,
2952 result.x, result.y );
2956 FreeLines( p_lines );
2958 xml_ReaderDelete( p_xml, p_xml_reader );
2960 xml_Delete( p_xml );
2962 stream_Delete( p_sub );
2968 static char* FontConfig_Select( FcConfig* priv, const char* family,
2969 bool b_bold, bool b_italic, int *i_idx )
2972 FcPattern *pat, *p_pat;
2976 pat = FcPatternCreate();
2977 if (!pat) return NULL;
2979 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2980 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2981 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2982 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2984 FcDefaultSubstitute( pat );
2986 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2988 FcPatternDestroy( pat );
2992 p_pat = FcFontMatch( priv, pat, &result );
2993 FcPatternDestroy( pat );
2994 if( !p_pat ) return NULL;
2996 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2997 || ( val_b != FcTrue ) )
2999 FcPatternDestroy( p_pat );
3002 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
3007 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
3009 FcPatternDestroy( p_pat );
3014 if( strcasecmp((const char*)val_s, family ) != 0 )
3015 msg_Warn( p_filter, "fontconfig: selected font family is not"
3016 "the requested one: '%s' != '%s'\n",
3017 (const char*)val_s, family );
3020 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
3022 FcPatternDestroy( p_pat );
3026 FcPatternDestroy( p_pat );
3027 return strdup( (const char*)val_s );
3031 static void FreeLine( line_desc_t *p_line )
3034 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
3036 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
3038 free( p_line->pp_glyphs );
3039 free( p_line->p_glyph_pos );
3040 free( p_line->p_fg_rgb );
3041 free( p_line->p_bg_rgb );
3042 free( p_line->p_fg_bg_ratio );
3043 free( p_line->pi_underline_offset );
3044 free( p_line->pi_underline_thickness );
3048 static void FreeLines( line_desc_t *p_lines )
3050 line_desc_t *p_line, *p_next;
3052 if( !p_lines ) return;
3054 for( p_line = p_lines; p_line != NULL; p_line = p_next )
3056 p_next = p_line->p_next;
3061 static line_desc_t *NewLine( int i_count )
3063 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
3065 if( !p_line ) return NULL;
3066 p_line->i_height = 0;
3067 p_line->i_width = 0;
3068 p_line->p_next = NULL;
3070 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
3071 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
3072 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
3073 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
3074 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
3075 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
3076 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
3077 if( ( p_line->pp_glyphs == NULL ) ||
3078 ( p_line->p_glyph_pos == NULL ) ||
3079 ( p_line->p_fg_rgb == NULL ) ||
3080 ( p_line->p_bg_rgb == NULL ) ||
3081 ( p_line->p_fg_bg_ratio == NULL ) ||
3082 ( p_line->pi_underline_offset == NULL ) ||
3083 ( p_line->pi_underline_thickness == NULL ) )
3085 free( p_line->pi_underline_thickness );
3086 free( p_line->pi_underline_offset );
3087 free( p_line->p_fg_rgb );
3088 free( p_line->p_bg_rgb );
3089 free( p_line->p_fg_bg_ratio );
3090 free( p_line->p_glyph_pos );
3091 free( p_line->pp_glyphs );
3095 p_line->pp_glyphs[0] = NULL;
3096 p_line->b_new_color_mode = false;
3101 static int GetFontSize( filter_t *p_filter )
3103 filter_sys_t *p_sys = p_filter->p_sys;
3107 if( p_sys->i_default_font_size )
3109 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
3110 i_size = p_sys->i_default_font_size * val.i_int / 1000;
3112 i_size = p_sys->i_default_font_size;
3116 var_Get( p_filter, "freetype-rel-fontsize", &val );
3119 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
3120 p_filter->p_sys->i_display_height =
3121 p_filter->fmt_out.video.i_height;
3126 msg_Warn( p_filter, "invalid fontsize, using 12" );
3127 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
3128 i_size = 12 * val.i_int / 1000;
3135 static int SetFontSize( filter_t *p_filter, int i_size )
3137 filter_sys_t *p_sys = p_filter->p_sys;
3141 i_size = GetFontSize( p_filter );
3143 msg_Dbg( p_filter, "using fontsize: %i", i_size );
3146 p_sys->i_font_size = i_size;
3148 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
3150 msg_Err( p_filter, "couldn't set font size to %d", i_size );
3151 return VLC_EGENERIC;
3157 static void YUVFromRGB( uint32_t i_argb,
3158 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
3160 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
3161 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
3162 int i_blue = ( i_argb & 0x000000ff );
3164 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
3165 802 * i_blue + 4096 + 131072 ) >> 13, 235);
3166 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
3167 3598 * i_blue + 4096 + 1048576) >> 13, 240);
3168 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
3169 -585 * i_blue + 4096 + 1048576) >> 13, 240);