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 typedef struct line_desc_t line_desc_t
;
80 /*****************************************************************************
82 *****************************************************************************/
83 static int Create ( vlc_object_t
* );
84 static void Destroy( vlc_object_t
* );
86 static int LoadFontsFromAttachments( filter_t
*p_filter
);
88 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
89 static int RenderText( filter_t
*, subpicture_region_t
*,
90 subpicture_region_t
* );
91 #ifdef HAVE_FONTCONFIG
92 static int RenderHtml( filter_t
*, subpicture_region_t
*,
93 subpicture_region_t
* );
94 static char *FontConfig_Select( FcConfig
*, const char *,
96 static int BuildDone( vlc_object_t
*, const char *, vlc_value_t
, vlc_value_t
,
99 static line_desc_t
*NewLine( int );
101 static int GetFontSize( filter_t
*p_filter
);
102 static int SetFontSize( filter_t
*, int );
103 static void YUVFromRGB( uint32_t i_argb
,
104 uint8_t *pi_y
, uint8_t *pi_u
, uint8_t *pi_v
);
106 /*****************************************************************************
108 *****************************************************************************/
109 #define FONT_TEXT N_("Font")
110 #define FONT_LONGTEXT N_("Filename for the font you want to use")
111 #define FONTSIZE_TEXT N_("Font size in pixels")
112 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
113 "that will be rendered on the video. " \
114 "If set to something different than 0 this option will override the " \
115 "relative font size." )
116 #define OPACITY_TEXT N_("Opacity")
117 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
118 "text that will be rendered on the video. 0 = transparent, " \
119 "255 = totally opaque. " )
120 #define COLOR_TEXT N_("Text default color")
121 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
122 "the video. This must be an hexadecimal (like HTML colors). The first two "\
123 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
124 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
125 #define FONTSIZER_TEXT N_("Relative font size")
126 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
127 "fonts that will be rendered on the video. If absolute font size is set, "\
128 "relative size will be overriden." )
130 static const int pi_sizes
[] = { 20, 18, 16, 12, 6 };
131 static const char *const ppsz_sizes_text
[] = {
132 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
133 #define YUVP_TEXT N_("Use YUVP renderer")
134 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
135 "This option is only needed if you want to encode into DVB subtitles" )
136 #define EFFECT_TEXT N_("Font Effect")
137 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
138 "text to improve its readability." )
140 #define EFFECT_BACKGROUND 1
141 #define EFFECT_OUTLINE 2
142 #define EFFECT_OUTLINE_FAT 3
144 static int const pi_effects
[] = { 1, 2, 3 };
145 static const char *const ppsz_effects_text
[] = {
146 N_("Background"),N_("Outline"), N_("Fat Outline") };
147 static const int pi_color_values
[] = {
148 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
149 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
150 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
152 static const char *const ppsz_color_descriptions
[] = {
153 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
154 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
155 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
158 set_shortname( N_("Text renderer"));
159 set_description( N_("Freetype2 font renderer") );
160 set_category( CAT_VIDEO
);
161 set_subcategory( SUBCAT_VIDEO_SUBPIC
);
163 add_file( "freetype-font", DEFAULT_FONT
, NULL
, FONT_TEXT
, FONT_LONGTEXT
,
166 add_integer( "freetype-fontsize", 0, NULL
, FONTSIZE_TEXT
,
167 FONTSIZE_LONGTEXT
, true );
169 /* opacity valid on 0..255, with default 255 = fully opaque */
170 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL
,
171 OPACITY_TEXT
, OPACITY_LONGTEXT
, true );
173 /* hook to the color values list, with default 0x00ffffff = white */
174 add_integer( "freetype-color", 0x00FFFFFF, NULL
, COLOR_TEXT
,
175 COLOR_LONGTEXT
, false );
176 change_integer_list( pi_color_values
, ppsz_color_descriptions
, 0 );
178 add_integer( "freetype-rel-fontsize", 16, NULL
, FONTSIZER_TEXT
,
179 FONTSIZER_LONGTEXT
, false );
180 change_integer_list( pi_sizes
, ppsz_sizes_text
, 0 );
181 add_integer( "freetype-effect", 2, NULL
, EFFECT_TEXT
,
182 EFFECT_LONGTEXT
, false );
183 change_integer_list( pi_effects
, ppsz_effects_text
, 0 );
185 add_bool( "freetype-yuvp", 0, NULL
, YUVP_TEXT
,
186 YUVP_LONGTEXT
, true );
187 set_capability( "text renderer", 100 );
188 add_shortcut( "text" );
189 set_callbacks( Create
, Destroy
);
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
;
218 typedef struct font_stack_t font_stack_t
;
223 uint32_t i_color
; /* ARGB */
224 uint32_t i_karaoke_bg_color
; /* ARGB */
226 font_stack_t
*p_next
;
232 uint32_t i_font_color
; /* ARGB */
233 uint32_t i_karaoke_bg_color
; /* ARGB */
240 static int Render( filter_t
*, subpicture_region_t
*, line_desc_t
*, int, int);
241 static void FreeLines( line_desc_t
* );
242 static void FreeLine( line_desc_t
* );
243 #ifdef HAVE_FONTCONFIG
244 static void FontBuilder( vlc_object_t
*p_this
);
247 /*****************************************************************************
248 * filter_sys_t: freetype local data
249 *****************************************************************************
250 * This structure is part of the video output thread descriptor.
251 * It describes the freetype specific properties of an output thread.
252 *****************************************************************************/
255 FT_Library p_library
; /* handle to library */
256 FT_Face p_face
; /* handle to face object */
258 uint8_t i_font_opacity
;
263 int i_default_font_size
;
264 int i_display_height
;
265 #ifdef HAVE_FONTCONFIG
266 FcConfig
*p_fontconfig
;
267 bool b_fontconfig_ok
;
268 vlc_mutex_t fontconfig_lock
;
271 input_attachment_t
**pp_font_attachments
;
272 int i_font_attachments
;
275 /*****************************************************************************
276 * Create: allocates osd-text video thread output method
277 *****************************************************************************
278 * This function allocates and initializes a Clone vout method.
279 *****************************************************************************/
280 static int Create( vlc_object_t
*p_this
)
282 filter_t
*p_filter
= (filter_t
*)p_this
;
284 char *psz_fontfile
= NULL
;
287 vlc_object_t
*p_fontbuilder
;
289 /* Allocate structure */
290 p_filter
->p_sys
= p_sys
= malloc( sizeof( filter_sys_t
) );
293 msg_Err( p_filter
, "out of memory" );
297 p_sys
->p_library
= 0;
298 p_sys
->i_font_size
= 0;
299 p_sys
->i_display_height
= 0;
301 var_Create( p_filter
, "freetype-font",
302 VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
303 var_Create( p_filter
, "freetype-fontsize",
304 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
305 var_Create( p_filter
, "freetype-rel-fontsize",
306 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
307 var_Create( p_filter
, "freetype-opacity",
308 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
309 var_Create( p_filter
, "freetype-effect",
310 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
311 var_Get( p_filter
, "freetype-opacity", &val
);
312 p_sys
->i_font_opacity
= __MAX( __MIN( val
.i_int
, 255 ), 0 );
313 var_Create( p_filter
, "freetype-color",
314 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
315 var_Get( p_filter
, "freetype-color", &val
);
316 p_sys
->i_font_color
= __MAX( __MIN( val
.i_int
, 0xFFFFFF ), 0 );
317 p_sys
->i_effect
= var_GetInteger( p_filter
, "freetype-effect" );
319 /* Look what method was requested */
320 var_Get( p_filter
, "freetype-font", &val
);
321 psz_fontfile
= val
.psz_string
;
322 if( !psz_fontfile
|| !*psz_fontfile
)
324 free( psz_fontfile
);
325 psz_fontfile
= (char *)malloc( PATH_MAX
+ 1 );
328 msg_Err( p_filter
, "out of memory" );
332 GetWindowsDirectory( psz_fontfile
, PATH_MAX
+ 1 );
333 strcat( psz_fontfile
, "\\fonts\\arial.ttf" );
334 #elif defined(__APPLE__)
335 strcpy( psz_fontfile
, DEFAULT_FONT
);
337 msg_Err( p_filter
, "user didn't specify a font" );
342 i_error
= FT_Init_FreeType( &p_sys
->p_library
);
345 msg_Err( p_filter
, "couldn't initialize freetype" );
348 i_error
= FT_New_Face( p_sys
->p_library
, psz_fontfile
? psz_fontfile
: "",
350 if( i_error
== FT_Err_Unknown_File_Format
)
352 msg_Err( p_filter
, "file %s have unknown format", psz_fontfile
);
357 msg_Err( p_filter
, "failed to load font file %s", psz_fontfile
);
361 i_error
= FT_Select_Charmap( p_sys
->p_face
, ft_encoding_unicode
);
364 msg_Err( p_filter
, "font has no unicode translation table" );
368 #ifdef HAVE_FONTCONFIG
369 vlc_mutex_init( &p_sys
->fontconfig_lock
);
370 p_sys
->b_fontconfig_ok
= false;
371 p_sys
->p_fontconfig
= NULL
;
373 /* Check for an existing Fontbuilder thread */
374 vlc_mutex_t
*lock
= var_AcquireMutex( "fontbuilder" );
375 p_fontbuilder
= vlc_object_find_name( p_filter
->p_libvlc
,
379 if( ! p_fontbuilder
)
381 /* Create the FontBuilder thread as a child of a top-level
382 * object, so that it can survive the destruction of the
383 * freetype object - the fontlist only needs to be built once,
384 * and calling the fontbuild a second time while the first is
385 * still in progress can cause thread instabilities.
388 p_fontbuilder
= vlc_object_create( p_filter
->p_libvlc
,
389 VLC_OBJECT_GENERIC
);
392 p_fontbuilder
->psz_object_name
= strdup( "fontlist builder" );
393 vlc_object_attach( p_fontbuilder
, p_filter
->p_libvlc
);
395 var_Create( p_fontbuilder
, "build-done", VLC_VAR_BOOL
);
396 var_SetBool( p_fontbuilder
, "build-done", false );
397 var_AddCallback( p_fontbuilder
, "build-done", BuildDone
, p_sys
);
399 if( vlc_thread_create( p_fontbuilder
,
402 VLC_THREAD_PRIORITY_LOW
,
405 /* Don't destroy the fontconfig object - we won't be able to do
406 * italics or bold or change the font face, but we will still
407 * be able to do underline and change the font size.
409 msg_Warn( p_filter
, "fontconfig database builder thread can't "
410 "be launched. Font styling support will be limited." );
415 vlc_object_release( p_fontbuilder
);
420 vlc_object_release( p_fontbuilder
);
422 vlc_mutex_unlock( lock
);
426 p_sys
->i_use_kerning
= FT_HAS_KERNING( p_sys
->p_face
);
428 var_Get( p_filter
, "freetype-fontsize", &val
);
429 p_sys
->i_default_font_size
= val
.i_int
;
430 if( SetFontSize( p_filter
, 0 ) != VLC_SUCCESS
) goto error
;
432 free( psz_fontfile
);
434 p_sys
->pp_font_attachments
= NULL
;
435 p_sys
->i_font_attachments
= 0;
437 p_filter
->pf_render_text
= RenderText
;
438 #ifdef HAVE_FONTCONFIG
439 p_filter
->pf_render_html
= RenderHtml
;
441 p_filter
->pf_render_html
= NULL
;
444 LoadFontsFromAttachments( p_filter
);
449 if( p_sys
->p_face
) FT_Done_Face( p_sys
->p_face
);
450 if( p_sys
->p_library
) FT_Done_FreeType( p_sys
->p_library
);
451 free( psz_fontfile
);
456 /*****************************************************************************
457 * Destroy: destroy Clone video thread output method
458 *****************************************************************************
459 * Clean up all data and library connections
460 *****************************************************************************/
461 static void Destroy( vlc_object_t
*p_this
)
463 filter_t
*p_filter
= (filter_t
*)p_this
;
464 filter_sys_t
*p_sys
= p_filter
->p_sys
;
466 if( p_sys
->pp_font_attachments
)
470 for( k
= 0; k
< p_sys
->i_font_attachments
; k
++ )
472 vlc_input_attachment_Delete( p_sys
->pp_font_attachments
[k
] );
475 free( p_sys
->pp_font_attachments
);
478 #ifdef HAVE_FONTCONFIG
479 vlc_mutex_t
*lock
= var_AcquireMutex( "fontbuilder" );
480 vlc_object_t
*p_fontbuilder
= vlc_object_find_name( p_filter
->p_libvlc
,
481 "fontlist builder", FIND_CHILD
);
484 var_DelCallback( p_fontbuilder
, "build-done", BuildDone
, p_sys
);
485 vlc_object_release( p_fontbuilder
);
487 vlc_mutex_unlock( lock
);
489 vlc_mutex_destroy( &p_sys
->fontconfig_lock
);
491 if( p_sys
->p_fontconfig
)
493 FcConfigDestroy( p_sys
->p_fontconfig
);
494 p_sys
->p_fontconfig
= NULL
;
496 /* FcFini asserts calling the subfunction FcCacheFini()
497 * even if no other library functions have been made since FcInit(),
498 * so don't call it. */
500 FT_Done_Face( p_sys
->p_face
);
501 FT_Done_FreeType( p_sys
->p_library
);
505 #ifdef HAVE_FONTCONFIG
507 static void FontBuilder( vlc_object_t
*p_this
)
509 FcConfig
*p_fontconfig
= FcInitLoadConfig();
512 vlc_thread_ready( p_this
);
518 msg_Dbg( p_this
, "Building font database..." );
520 if(! FcConfigBuildFonts( p_fontconfig
))
522 /* Don't destroy the fontconfig object - we won't be able to do
523 * italics or bold or change the font face, but we will still
524 * be able to do underline and change the font size.
526 msg_Err( p_this
, "fontconfig database can't be built. "
527 "Font styling won't be available" );
531 msg_Dbg( p_this
, "Finished building font database." );
532 msg_Dbg( p_this
, "Took %ld seconds", (long)((t2
- t1
)/1000000) );
534 lock
= var_AcquireMutex( "fontbuilder" );
536 var_SetBool( p_this
, "build-done", true );
538 FcConfigDestroy( p_fontconfig
);
539 vlc_mutex_unlock( lock
);
541 vlc_object_detach( p_this
);
542 vlc_object_release( p_this
);
547 /*****************************************************************************
548 * Make any TTF/OTF fonts present in the attachments of the media file
549 * and store them for later use by the FreeType Engine
550 *****************************************************************************/
551 static int LoadFontsFromAttachments( filter_t
*p_filter
)
553 filter_sys_t
*p_sys
= p_filter
->p_sys
;
554 input_thread_t
*p_input
;
555 input_attachment_t
**pp_attachments
;
556 int i_attachments_cnt
;
558 int rv
= VLC_SUCCESS
;
560 p_input
= (input_thread_t
*)vlc_object_find( p_filter
, VLC_OBJECT_INPUT
, FIND_PARENT
);
564 if( VLC_SUCCESS
!= input_Control( p_input
, INPUT_GET_ATTACHMENTS
, &pp_attachments
, &i_attachments_cnt
))
566 vlc_object_release(p_input
);
570 p_sys
->i_font_attachments
= 0;
571 p_sys
->pp_font_attachments
= malloc( i_attachments_cnt
* sizeof( input_attachment_t
* ));
572 if(! p_sys
->pp_font_attachments
)
575 for( k
= 0; k
< i_attachments_cnt
; k
++ )
577 input_attachment_t
*p_attach
= pp_attachments
[k
];
579 if( p_sys
->pp_font_attachments
)
581 if(( !strcmp( p_attach
->psz_mime
, "application/x-truetype-font" ) || // TTF
582 !strcmp( p_attach
->psz_mime
, "application/x-font-otf" ) ) && // OTF
583 ( p_attach
->i_data
> 0 ) &&
584 ( p_attach
->p_data
!= NULL
) )
586 p_sys
->pp_font_attachments
[ p_sys
->i_font_attachments
++ ] = p_attach
;
590 vlc_input_attachment_Delete( p_attach
);
595 vlc_input_attachment_Delete( p_attach
);
598 free( pp_attachments
);
600 vlc_object_release(p_input
);
605 /*****************************************************************************
606 * Render: place string in picture
607 *****************************************************************************
608 * This function merges the previously rendered freetype glyphs into a picture
609 *****************************************************************************/
610 static int Render( filter_t
*p_filter
, subpicture_region_t
*p_region
,
611 line_desc_t
*p_line
, int i_width
, int i_height
)
613 static uint8_t pi_gamma
[16] =
614 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
615 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
619 int i
, x
, y
, i_pitch
;
620 uint8_t i_y
; /* YUV values, derived from incoming RGB */
622 subpicture_region_t
*p_region_tmp
;
624 /* Create a new subpicture region */
625 memset( &fmt
, 0, sizeof(video_format_t
) );
626 fmt
.i_chroma
= VLC_FOURCC('Y','U','V','P');
628 fmt
.i_width
= fmt
.i_visible_width
= i_width
+ 4;
629 fmt
.i_height
= fmt
.i_visible_height
= i_height
+ 4;
630 if( p_region
->fmt
.i_visible_width
> 0 )
631 fmt
.i_visible_width
= p_region
->fmt
.i_visible_width
;
632 if( p_region
->fmt
.i_visible_height
> 0 )
633 fmt
.i_visible_height
= p_region
->fmt
.i_visible_height
;
634 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
635 p_region_tmp
= spu_CreateRegion( p_filter
, &fmt
);
638 msg_Err( p_filter
, "cannot allocate SPU region" );
642 p_region
->fmt
= p_region_tmp
->fmt
;
643 p_region
->picture
= p_region_tmp
->picture
;
644 free( p_region_tmp
);
646 /* Calculate text color components */
647 i_y
= (uint8_t)(( 66 * p_line
->i_red
+ 129 * p_line
->i_green
+
648 25 * p_line
->i_blue
+ 128) >> 8) + 16;
649 i_u
= (int8_t)(( -38 * p_line
->i_red
- 74 * p_line
->i_green
+
650 112 * p_line
->i_blue
+ 128) >> 8) + 128;
651 i_v
= (int8_t)(( 112 * p_line
->i_red
- 94 * p_line
->i_green
-
652 18 * p_line
->i_blue
+ 128) >> 8) + 128;
655 fmt
.p_palette
->i_entries
= 16;
656 for( i
= 0; i
< 8; i
++ )
658 fmt
.p_palette
->palette
[i
][0] = 0;
659 fmt
.p_palette
->palette
[i
][1] = 0x80;
660 fmt
.p_palette
->palette
[i
][2] = 0x80;
661 fmt
.p_palette
->palette
[i
][3] = pi_gamma
[i
];
662 fmt
.p_palette
->palette
[i
][3] =
663 (int)fmt
.p_palette
->palette
[i
][3] * (255 - p_line
->i_alpha
) / 255;
665 for( i
= 8; i
< fmt
.p_palette
->i_entries
; i
++ )
667 fmt
.p_palette
->palette
[i
][0] = i
* 16 * i_y
/ 256;
668 fmt
.p_palette
->palette
[i
][1] = i_u
;
669 fmt
.p_palette
->palette
[i
][2] = i_v
;
670 fmt
.p_palette
->palette
[i
][3] = pi_gamma
[i
];
671 fmt
.p_palette
->palette
[i
][3] =
672 (int)fmt
.p_palette
->palette
[i
][3] * (255 - p_line
->i_alpha
) / 255;
675 p_dst
= p_region
->picture
.Y_PIXELS
;
676 i_pitch
= p_region
->picture
.Y_PITCH
;
678 /* Initialize the region pixels */
679 memset( p_dst
, 0, i_pitch
* p_region
->fmt
.i_height
);
681 for( ; p_line
!= NULL
; p_line
= p_line
->p_next
)
683 int i_glyph_tmax
= 0;
684 int i_bitmap_offset
, i_offset
, i_align_offset
= 0;
685 for( i
= 0; p_line
->pp_glyphs
[i
] != NULL
; i
++ )
687 FT_BitmapGlyph p_glyph
= p_line
->pp_glyphs
[ i
];
688 i_glyph_tmax
= __MAX( i_glyph_tmax
, p_glyph
->top
);
691 if( p_line
->i_width
< i_width
)
693 if( (p_region
->i_align
& 0x3) == SUBPICTURE_ALIGN_RIGHT
)
695 i_align_offset
= i_width
- p_line
->i_width
;
697 else if( (p_region
->i_align
& 0x3) != SUBPICTURE_ALIGN_LEFT
)
699 i_align_offset
= ( i_width
- p_line
->i_width
) / 2;
703 for( i
= 0; p_line
->pp_glyphs
[i
] != NULL
; i
++ )
705 FT_BitmapGlyph p_glyph
= p_line
->pp_glyphs
[ i
];
707 i_offset
= ( p_line
->p_glyph_pos
[ i
].y
+
708 i_glyph_tmax
- p_glyph
->top
+ 2 ) *
709 i_pitch
+ p_line
->p_glyph_pos
[ i
].x
+ p_glyph
->left
+ 2 +
712 for( y
= 0, i_bitmap_offset
= 0; y
< p_glyph
->bitmap
.rows
; y
++ )
714 for( x
= 0; x
< p_glyph
->bitmap
.width
; x
++, i_bitmap_offset
++ )
716 if( p_glyph
->bitmap
.buffer
[i_bitmap_offset
] )
718 ((int)p_glyph
->bitmap
.buffer
[i_bitmap_offset
] + 8)/16;
725 /* Outlining (find something better than nearest neighbour filtering ?) */
728 uint8_t *p_dst
= p_region
->picture
.Y_PIXELS
;
729 uint8_t *p_top
= p_dst
; /* Use 1st line as a cache */
730 uint8_t left
, current
;
732 for( y
= 1; y
< (int)fmt
.i_height
- 1; y
++ )
734 if( y
> 1 ) memcpy( p_top
, p_dst
, fmt
.i_width
);
735 p_dst
+= p_region
->picture
.Y_PITCH
;
738 for( x
= 1; x
< (int)fmt
.i_width
- 1; x
++ )
741 p_dst
[x
] = ( 8 * (int)p_dst
[x
] + left
+ p_dst
[x
+1] + p_top
[x
-1]+ p_top
[x
] + p_top
[x
+1] +
742 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;
746 memset( p_top
, 0, fmt
.i_width
);
752 static void UnderlineGlyphYUVA( int i_line_thickness
, int i_line_offset
, bool b_ul_next_char
,
753 FT_BitmapGlyph p_this_glyph
, FT_Vector
*p_this_glyph_pos
,
754 FT_BitmapGlyph p_next_glyph
, FT_Vector
*p_next_glyph_pos
,
755 int i_glyph_tmax
, int i_align_offset
,
756 uint8_t i_y
, uint8_t i_u
, uint8_t i_v
,
757 subpicture_region_t
*p_region
)
761 uint8_t *p_dst_y
,*p_dst_u
,*p_dst_v
,*p_dst_a
;
763 p_dst_y
= p_region
->picture
.Y_PIXELS
;
764 p_dst_u
= p_region
->picture
.U_PIXELS
;
765 p_dst_v
= p_region
->picture
.V_PIXELS
;
766 p_dst_a
= p_region
->picture
.A_PIXELS
;
767 i_pitch
= p_region
->picture
.A_PITCH
;
769 int i_offset
= ( p_this_glyph_pos
->y
+ i_glyph_tmax
+ i_line_offset
+ 3 ) * i_pitch
+
770 p_this_glyph_pos
->x
+ p_this_glyph
->left
+ 3 + i_align_offset
;
772 for( y
= 0; y
< i_line_thickness
; y
++ )
774 int i_extra
= p_this_glyph
->bitmap
.width
;
778 i_extra
= (p_next_glyph_pos
->x
+ p_next_glyph
->left
) -
779 (p_this_glyph_pos
->x
+ p_this_glyph
->left
);
781 for( x
= 0; x
< i_extra
; x
++ )
785 /* break the underline around the tails of any glyphs which cross it */
786 for( z
= x
- i_line_thickness
;
787 z
< x
+ i_line_thickness
&& b_ok
;
790 if( p_next_glyph
&& ( z
>= i_extra
) )
792 int i_row
= i_line_offset
+ p_next_glyph
->top
+ y
;
794 if( ( p_next_glyph
->bitmap
.rows
> i_row
) &&
795 p_next_glyph
->bitmap
.buffer
[p_next_glyph
->bitmap
.width
* i_row
+ z
-i_extra
] )
800 else if ((z
> 0 ) && (z
< p_this_glyph
->bitmap
.width
))
802 int i_row
= i_line_offset
+ p_this_glyph
->top
+ y
;
804 if( ( p_this_glyph
->bitmap
.rows
> i_row
) &&
805 p_this_glyph
->bitmap
.buffer
[p_this_glyph
->bitmap
.width
* i_row
+ z
] )
814 p_dst_y
[i_offset
+x
] = (i_y
* 255) >> 8;
815 p_dst_u
[i_offset
+x
] = i_u
;
816 p_dst_v
[i_offset
+x
] = i_v
;
817 p_dst_a
[i_offset
+x
] = 255;
824 static void DrawBlack( line_desc_t
*p_line
, int i_width
, subpicture_region_t
*p_region
, int xoffset
, int yoffset
)
826 uint8_t *p_dst
= p_region
->picture
.A_PIXELS
;
827 int i_pitch
= p_region
->picture
.A_PITCH
;
830 for( ; p_line
!= NULL
; p_line
= p_line
->p_next
)
832 int i_glyph_tmax
=0, i
= 0;
833 int i_bitmap_offset
, i_offset
, i_align_offset
= 0;
834 for( i
= 0; p_line
->pp_glyphs
[i
] != NULL
; i
++ )
836 FT_BitmapGlyph p_glyph
= p_line
->pp_glyphs
[ i
];
837 i_glyph_tmax
= __MAX( i_glyph_tmax
, p_glyph
->top
);
840 if( p_line
->i_width
< i_width
)
842 if( (p_region
->i_align
& 0x3) == SUBPICTURE_ALIGN_RIGHT
)
844 i_align_offset
= i_width
- p_line
->i_width
;
846 else if( (p_region
->i_align
& 0x3) != SUBPICTURE_ALIGN_LEFT
)
848 i_align_offset
= ( i_width
- p_line
->i_width
) / 2;
852 for( i
= 0; p_line
->pp_glyphs
[i
] != NULL
; i
++ )
854 FT_BitmapGlyph p_glyph
= p_line
->pp_glyphs
[ i
];
856 i_offset
= ( p_line
->p_glyph_pos
[ i
].y
+
857 i_glyph_tmax
- p_glyph
->top
+ 3 + yoffset
) *
858 i_pitch
+ p_line
->p_glyph_pos
[ i
].x
+ p_glyph
->left
+ 3 +
859 i_align_offset
+xoffset
;
861 for( y
= 0, i_bitmap_offset
= 0; y
< p_glyph
->bitmap
.rows
; y
++ )
863 for( x
= 0; x
< p_glyph
->bitmap
.width
; x
++, i_bitmap_offset
++ )
865 if( p_glyph
->bitmap
.buffer
[i_bitmap_offset
] )
866 if( p_dst
[i_offset
+x
] <
867 ((int)p_glyph
->bitmap
.buffer
[i_bitmap_offset
]) )
869 ((int)p_glyph
->bitmap
.buffer
[i_bitmap_offset
]);
878 /*****************************************************************************
879 * Render: place string in picture
880 *****************************************************************************
881 * This function merges the previously rendered freetype glyphs into a picture
882 *****************************************************************************/
883 static int RenderYUVA( filter_t
*p_filter
, subpicture_region_t
*p_region
,
884 line_desc_t
*p_line
, int i_width
, int i_height
)
886 uint8_t *p_dst_y
,*p_dst_u
,*p_dst_v
,*p_dst_a
;
888 int i
, x
, y
, i_pitch
, i_alpha
;
889 uint8_t i_y
, i_u
, i_v
; /* YUV values, derived from incoming RGB */
890 subpicture_region_t
*p_region_tmp
;
892 if( i_width
== 0 || i_height
== 0 )
895 /* Create a new subpicture region */
896 memset( &fmt
, 0, sizeof(video_format_t
) );
897 fmt
.i_chroma
= VLC_FOURCC('Y','U','V','A');
899 fmt
.i_width
= fmt
.i_visible_width
= i_width
+ 6;
900 fmt
.i_height
= fmt
.i_visible_height
= i_height
+ 6;
901 if( p_region
->fmt
.i_visible_width
> 0 )
902 fmt
.i_visible_width
= p_region
->fmt
.i_visible_width
;
903 if( p_region
->fmt
.i_visible_height
> 0 )
904 fmt
.i_visible_height
= p_region
->fmt
.i_visible_height
;
905 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
906 p_region_tmp
= spu_CreateRegion( p_filter
, &fmt
);
909 msg_Err( p_filter
, "cannot allocate SPU region" );
913 p_region
->fmt
= p_region_tmp
->fmt
;
914 p_region
->picture
= p_region_tmp
->picture
;
915 free( p_region_tmp
);
917 /* Calculate text color components */
918 YUVFromRGB( (p_line
->i_red
<< 16) |
919 (p_line
->i_green
<< 8) |
922 i_alpha
= p_line
->i_alpha
;
924 p_dst_y
= p_region
->picture
.Y_PIXELS
;
925 p_dst_u
= p_region
->picture
.U_PIXELS
;
926 p_dst_v
= p_region
->picture
.V_PIXELS
;
927 p_dst_a
= p_region
->picture
.A_PIXELS
;
928 i_pitch
= p_region
->picture
.A_PITCH
;
930 /* Initialize the region pixels */
931 if( p_filter
->p_sys
->i_effect
!= EFFECT_BACKGROUND
)
933 memset( p_dst_y
, 0x00, i_pitch
* p_region
->fmt
.i_height
);
934 memset( p_dst_u
, 0x80, i_pitch
* p_region
->fmt
.i_height
);
935 memset( p_dst_v
, 0x80, i_pitch
* p_region
->fmt
.i_height
);
936 memset( p_dst_a
, 0, i_pitch
* p_region
->fmt
.i_height
);
940 memset( p_dst_y
, 0x0, i_pitch
* p_region
->fmt
.i_height
);
941 memset( p_dst_u
, 0x80, i_pitch
* p_region
->fmt
.i_height
);
942 memset( p_dst_v
, 0x80, i_pitch
* p_region
->fmt
.i_height
);
943 memset( p_dst_a
, 0x80, i_pitch
* p_region
->fmt
.i_height
);
945 if( p_filter
->p_sys
->i_effect
== EFFECT_OUTLINE
||
946 p_filter
->p_sys
->i_effect
== EFFECT_OUTLINE_FAT
)
948 DrawBlack( p_line
, i_width
, p_region
, 0, 0);
949 DrawBlack( p_line
, i_width
, p_region
, -1, 0);
950 DrawBlack( p_line
, i_width
, p_region
, 0, -1);
951 DrawBlack( p_line
, i_width
, p_region
, 1, 0);
952 DrawBlack( p_line
, i_width
, p_region
, 0, 1);
955 if( p_filter
->p_sys
->i_effect
== EFFECT_OUTLINE_FAT
)
957 DrawBlack( p_line
, i_width
, p_region
, -1, -1);
958 DrawBlack( p_line
, i_width
, p_region
, -1, 1);
959 DrawBlack( p_line
, i_width
, p_region
, 1, -1);
960 DrawBlack( p_line
, i_width
, p_region
, 1, 1);
962 DrawBlack( p_line
, i_width
, p_region
, -2, 0);
963 DrawBlack( p_line
, i_width
, p_region
, 0, -2);
964 DrawBlack( p_line
, i_width
, p_region
, 2, 0);
965 DrawBlack( p_line
, i_width
, p_region
, 0, 2);
967 DrawBlack( p_line
, i_width
, p_region
, -2, -2);
968 DrawBlack( p_line
, i_width
, p_region
, -2, 2);
969 DrawBlack( p_line
, i_width
, p_region
, 2, -2);
970 DrawBlack( p_line
, i_width
, p_region
, 2, 2);
972 DrawBlack( p_line
, i_width
, p_region
, -3, 0);
973 DrawBlack( p_line
, i_width
, p_region
, 0, -3);
974 DrawBlack( p_line
, i_width
, p_region
, 3, 0);
975 DrawBlack( p_line
, i_width
, p_region
, 0, 3);
978 for( ; p_line
!= NULL
; p_line
= p_line
->p_next
)
980 int i_glyph_tmax
= 0;
981 int i_bitmap_offset
, i_offset
, i_align_offset
= 0;
982 for( i
= 0; p_line
->pp_glyphs
[i
] != NULL
; i
++ )
984 FT_BitmapGlyph p_glyph
= p_line
->pp_glyphs
[ i
];
985 i_glyph_tmax
= __MAX( i_glyph_tmax
, p_glyph
->top
);
988 if( p_line
->i_width
< i_width
)
990 if( (p_region
->i_align
& 0x3) == SUBPICTURE_ALIGN_RIGHT
)
992 i_align_offset
= i_width
- p_line
->i_width
;
994 else if( (p_region
->i_align
& 0x3) != SUBPICTURE_ALIGN_LEFT
)
996 i_align_offset
= ( i_width
- p_line
->i_width
) / 2;
1000 for( i
= 0; p_line
->pp_glyphs
[i
] != NULL
; i
++ )
1002 FT_BitmapGlyph p_glyph
= p_line
->pp_glyphs
[ i
];
1004 i_offset
= ( p_line
->p_glyph_pos
[ i
].y
+
1005 i_glyph_tmax
- p_glyph
->top
+ 3 ) *
1006 i_pitch
+ p_line
->p_glyph_pos
[ i
].x
+ p_glyph
->left
+ 3 +
1009 if( p_line
->b_new_color_mode
)
1011 /* Every glyph can (and in fact must) have its own color */
1012 YUVFromRGB( p_line
->p_fg_rgb
[ i
], &i_y
, &i_u
, &i_v
);
1015 for( y
= 0, i_bitmap_offset
= 0; y
< p_glyph
->bitmap
.rows
; y
++ )
1017 for( x
= 0; x
< p_glyph
->bitmap
.width
; x
++, i_bitmap_offset
++ )
1019 uint8_t i_y_local
= i_y
;
1020 uint8_t i_u_local
= i_u
;
1021 uint8_t i_v_local
= i_v
;
1023 if( p_line
->p_fg_bg_ratio
!= 0x00 )
1025 int i_split
= p_glyph
->bitmap
.width
*
1026 p_line
->p_fg_bg_ratio
[ i
] / 0x7f;
1030 YUVFromRGB( p_line
->p_bg_rgb
[ i
],
1031 &i_y_local
, &i_u_local
, &i_v_local
);
1035 if( p_glyph
->bitmap
.buffer
[i_bitmap_offset
] )
1037 p_dst_y
[i_offset
+x
] = ((p_dst_y
[i_offset
+x
] *(255-(int)p_glyph
->bitmap
.buffer
[i_bitmap_offset
])) +
1038 i_y
* ((int)p_glyph
->bitmap
.buffer
[i_bitmap_offset
])) >> 8;
1040 p_dst_u
[i_offset
+x
] = i_u
;
1041 p_dst_v
[i_offset
+x
] = i_v
;
1043 if( p_filter
->p_sys
->i_effect
== EFFECT_BACKGROUND
)
1044 p_dst_a
[i_offset
+x
] = 0xff;
1047 i_offset
+= i_pitch
;
1050 if( p_line
->pi_underline_thickness
[ i
] )
1052 UnderlineGlyphYUVA( p_line
->pi_underline_thickness
[ i
],
1053 p_line
->pi_underline_offset
[ i
],
1054 (p_line
->pp_glyphs
[i
+1] && (p_line
->pi_underline_thickness
[ i
+ 1] > 0)),
1055 p_line
->pp_glyphs
[i
], &(p_line
->p_glyph_pos
[i
]),
1056 p_line
->pp_glyphs
[i
+1], &(p_line
->p_glyph_pos
[i
+1]),
1057 i_glyph_tmax
, i_align_offset
,
1064 /* Apply the alpha setting */
1065 for( i
= 0; i
< (int)fmt
.i_height
* i_pitch
; i
++ )
1066 p_dst_a
[i
] = p_dst_a
[i
] * (255 - i_alpha
) / 255;
1072 * This function renders a text subpicture region into another one.
1073 * It also calculates the size needed for this string, and renders the
1074 * needed glyphs into memory. It is used as pf_add_string callback in
1075 * the vout method by this module
1077 static int RenderText( filter_t
*p_filter
, subpicture_region_t
*p_region_out
,
1078 subpicture_region_t
*p_region_in
)
1080 filter_sys_t
*p_sys
= p_filter
->p_sys
;
1081 line_desc_t
*p_lines
= NULL
, *p_line
= NULL
, *p_next
= NULL
, *p_prev
= NULL
;
1082 int i
, i_pen_y
, i_pen_x
, i_error
, i_glyph_index
, i_previous
;
1083 uint32_t *psz_unicode
, *psz_unicode_orig
= NULL
, i_char
, *psz_line_start
;
1084 int i_string_length
;
1086 vlc_iconv_t iconv_handle
= (vlc_iconv_t
)(-1);
1087 int i_font_color
, i_font_alpha
, i_font_size
, i_red
, i_green
, i_blue
;
1097 if( !p_region_in
|| !p_region_out
) return VLC_EGENERIC
;
1098 psz_string
= p_region_in
->psz_text
;
1099 if( !psz_string
|| !*psz_string
) return VLC_EGENERIC
;
1101 if( VLC_SUCCESS
== var_Get( p_filter
, "scale", &val
))
1102 i_scale
= val
.i_int
;
1104 if( p_region_in
->p_style
)
1106 i_font_color
= __MAX( __MIN( p_region_in
->p_style
->i_font_color
, 0xFFFFFF ), 0 );
1107 i_font_alpha
= __MAX( __MIN( p_region_in
->p_style
->i_font_alpha
, 255 ), 0 );
1108 i_font_size
= __MAX( __MIN( p_region_in
->p_style
->i_font_size
, 255 ), 0 ) * i_scale
/ 1000;
1112 i_font_color
= p_sys
->i_font_color
;
1113 i_font_alpha
= 255 - p_sys
->i_font_opacity
;
1114 i_font_size
= p_sys
->i_default_font_size
* i_scale
/ 1000;
1117 if( i_font_color
== 0xFFFFFF ) i_font_color
= p_sys
->i_font_color
;
1118 if( !i_font_alpha
) i_font_alpha
= 255 - p_sys
->i_font_opacity
;
1119 SetFontSize( p_filter
, i_font_size
);
1121 i_red
= ( i_font_color
& 0x00FF0000 ) >> 16;
1122 i_green
= ( i_font_color
& 0x0000FF00 ) >> 8;
1123 i_blue
= i_font_color
& 0x000000FF;
1125 result
.x
= result
.y
= 0;
1126 line
.xMin
= line
.xMax
= line
.yMin
= line
.yMax
= 0;
1128 psz_unicode
= psz_unicode_orig
=
1129 malloc( ( strlen(psz_string
) + 1 ) * sizeof(uint32_t) );
1130 if( psz_unicode
== NULL
)
1132 msg_Err( p_filter
, "out of memory" );
1135 #if defined(WORDS_BIGENDIAN)
1136 iconv_handle
= vlc_iconv_open( "UCS-4BE", "UTF-8" );
1138 iconv_handle
= vlc_iconv_open( "UCS-4LE", "UTF-8" );
1140 if( iconv_handle
== (vlc_iconv_t
)-1 )
1142 msg_Warn( p_filter
, "unable to do conversion" );
1148 const char *p_in_buffer
= psz_string
;
1149 size_t i_in_bytes
, i_out_bytes
, i_out_bytes_left
, i_ret
;
1150 i_in_bytes
= strlen( psz_string
);
1151 i_out_bytes
= i_in_bytes
* sizeof( uint32_t );
1152 i_out_bytes_left
= i_out_bytes
;
1153 p_out_buffer
= (char *)psz_unicode
;
1154 i_ret
= vlc_iconv( iconv_handle
, (const char**)&p_in_buffer
,
1156 &p_out_buffer
, &i_out_bytes_left
);
1158 vlc_iconv_close( iconv_handle
);
1162 msg_Warn( p_filter
, "failed to convert string to unicode (%m), "
1163 "bytes left %u", (unsigned)i_in_bytes
);
1166 *(uint32_t*)p_out_buffer
= 0;
1167 i_string_length
= (i_out_bytes
- i_out_bytes_left
) / sizeof(uint32_t);
1170 #if defined(HAVE_FRIBIDI)
1172 uint32_t *p_fribidi_string
;
1173 int32_t start_pos
, pos
= 0;
1175 p_fribidi_string
= malloc( (i_string_length
+ 1) * sizeof(uint32_t) );
1176 if( !p_fribidi_string
)
1178 msg_Err( p_filter
, "out of memory" );
1182 /* Do bidi conversion line-by-line */
1183 while( pos
< i_string_length
)
1185 while( pos
< i_string_length
)
1187 i_char
= psz_unicode
[pos
];
1188 if (i_char
!= '\r' && i_char
!= '\n')
1190 p_fribidi_string
[pos
] = i_char
;
1194 while( pos
< i_string_length
)
1196 i_char
= psz_unicode
[pos
];
1197 if (i_char
== '\r' || i_char
== '\n')
1201 if (pos
> start_pos
)
1203 FriBidiCharType base_dir
= FRIBIDI_TYPE_LTR
;
1204 fribidi_log2vis((FriBidiChar
*)psz_unicode
+ start_pos
,
1207 (FriBidiChar
*)p_fribidi_string
+ start_pos
,
1212 free( psz_unicode_orig
);
1213 psz_unicode
= psz_unicode_orig
= p_fribidi_string
;
1214 p_fribidi_string
[ i_string_length
] = 0;
1218 /* Calculate relative glyph positions and a bounding box for the
1220 if( !(p_line
= NewLine( strlen( psz_string
))) )
1222 msg_Err( p_filter
, "out of memory" );
1226 i_pen_x
= i_pen_y
= 0;
1228 psz_line_start
= psz_unicode
;
1230 #define face p_sys->p_face
1231 #define glyph face->glyph
1233 while( *psz_unicode
)
1235 i_char
= *psz_unicode
++;
1236 if( i_char
== '\r' ) /* ignore CR chars wherever they may be */
1241 if( i_char
== '\n' )
1243 psz_line_start
= psz_unicode
;
1244 if( !(p_next
= NewLine( strlen( psz_string
))) )
1246 msg_Err( p_filter
, "out of memory" );
1249 p_line
->p_next
= p_next
;
1250 p_line
->i_width
= line
.xMax
;
1251 p_line
->i_height
= face
->size
->metrics
.height
>> 6;
1252 p_line
->pp_glyphs
[ i
] = NULL
;
1253 p_line
->i_alpha
= i_font_alpha
;
1254 p_line
->i_red
= i_red
;
1255 p_line
->i_green
= i_green
;
1256 p_line
->i_blue
= i_blue
;
1259 result
.x
= __MAX( result
.x
, line
.xMax
);
1260 result
.y
+= face
->size
->metrics
.height
>> 6;
1263 line
.xMin
= line
.xMax
= line
.yMin
= line
.yMax
= 0;
1264 i_pen_y
+= face
->size
->metrics
.height
>> 6;
1266 msg_Dbg( p_filter
, "Creating new line, i is %d", i
);
1271 i_glyph_index
= FT_Get_Char_Index( face
, i_char
);
1272 if( p_sys
->i_use_kerning
&& i_glyph_index
1276 FT_Get_Kerning( face
, i_previous
, i_glyph_index
,
1277 ft_kerning_default
, &delta
);
1278 i_pen_x
+= delta
.x
>> 6;
1281 p_line
->p_glyph_pos
[ i
].x
= i_pen_x
;
1282 p_line
->p_glyph_pos
[ i
].y
= i_pen_y
;
1283 i_error
= FT_Load_Glyph( face
, i_glyph_index
, FT_LOAD_DEFAULT
);
1286 msg_Err( p_filter
, "unable to render text FT_Load_Glyph returned"
1290 i_error
= FT_Get_Glyph( glyph
, &tmp_glyph
);
1293 msg_Err( p_filter
, "unable to render text FT_Get_Glyph returned "
1297 FT_Glyph_Get_CBox( tmp_glyph
, ft_glyph_bbox_pixels
, &glyph_size
);
1298 i_error
= FT_Glyph_To_Bitmap( &tmp_glyph
, ft_render_mode_normal
, 0, 1);
1301 FT_Done_Glyph( tmp_glyph
);
1304 p_line
->pp_glyphs
[ i
] = (FT_BitmapGlyph
)tmp_glyph
;
1307 line
.xMax
= p_line
->p_glyph_pos
[i
].x
+ glyph_size
.xMax
-
1308 glyph_size
.xMin
+ ((FT_BitmapGlyph
)tmp_glyph
)->left
;
1309 if( line
.xMax
> (int)p_filter
->fmt_out
.video
.i_visible_width
- 20 )
1311 FT_Done_Glyph( (FT_Glyph
)p_line
->pp_glyphs
[ i
] );
1312 p_line
->pp_glyphs
[ i
] = NULL
;
1314 p_line
= NewLine( strlen( psz_string
));
1315 if( p_prev
) p_prev
->p_next
= p_line
;
1316 else p_lines
= p_line
;
1318 uint32_t *psz_unicode_saved
= psz_unicode
;
1319 while( psz_unicode
> psz_line_start
&& *psz_unicode
!= ' ' )
1323 if( psz_unicode
== psz_line_start
)
1324 { /* try harder to break that line */
1325 psz_unicode
= psz_unicode_saved
;
1326 while( psz_unicode
> psz_line_start
&&
1327 *psz_unicode
!= '_' && *psz_unicode
!= '/' &&
1328 *psz_unicode
!= '\\' && *psz_unicode
!= '.' )
1333 if( psz_unicode
== psz_line_start
)
1335 msg_Warn( p_filter
, "unbreakable string" );
1340 *psz_unicode
= '\n';
1342 psz_unicode
= psz_line_start
;
1345 line
.xMin
= line
.xMax
= line
.yMin
= line
.yMax
= 0;
1348 line
.yMax
= __MAX( line
.yMax
, glyph_size
.yMax
);
1349 line
.yMin
= __MIN( line
.yMin
, glyph_size
.yMin
);
1351 i_previous
= i_glyph_index
;
1352 i_pen_x
+= glyph
->advance
.x
>> 6;
1356 p_line
->i_width
= line
.xMax
;
1357 p_line
->i_height
= face
->size
->metrics
.height
>> 6;
1358 p_line
->pp_glyphs
[ i
] = NULL
;
1359 p_line
->i_alpha
= i_font_alpha
;
1360 p_line
->i_red
= i_red
;
1361 p_line
->i_green
= i_green
;
1362 p_line
->i_blue
= i_blue
;
1363 result
.x
= __MAX( result
.x
, line
.xMax
);
1364 result
.y
+= line
.yMax
- line
.yMin
;
1369 p_region_out
->i_x
= p_region_in
->i_x
;
1370 p_region_out
->i_y
= p_region_in
->i_y
;
1372 if( config_GetInt( p_filter
, "freetype-yuvp" ) )
1373 Render( p_filter
, p_region_out
, p_lines
, result
.x
, result
.y
);
1375 RenderYUVA( p_filter
, p_region_out
, p_lines
, result
.x
, result
.y
);
1377 free( psz_unicode_orig
);
1378 FreeLines( p_lines
);
1382 free( psz_unicode_orig
);
1383 FreeLines( p_lines
);
1384 return VLC_EGENERIC
;
1387 #ifdef HAVE_FONTCONFIG
1388 static ft_style_t
*CreateStyle( char *psz_fontname
, int i_font_size
,
1389 uint32_t i_font_color
, uint32_t i_karaoke_bg_color
, bool b_bold
,
1390 bool b_italic
, bool b_uline
)
1392 ft_style_t
*p_style
= malloc( sizeof( ft_style_t
));
1396 p_style
->i_font_size
= i_font_size
;
1397 p_style
->i_font_color
= i_font_color
;
1398 p_style
->i_karaoke_bg_color
= i_karaoke_bg_color
;
1399 p_style
->b_italic
= b_italic
;
1400 p_style
->b_bold
= b_bold
;
1401 p_style
->b_underline
= b_uline
;
1403 p_style
->psz_fontname
= strdup( psz_fontname
);
1408 static void DeleteStyle( ft_style_t
*p_style
)
1412 free( p_style
->psz_fontname
);
1417 static bool StyleEquals( ft_style_t
*s1
, ft_style_t
*s2
)
1424 if(( s1
->i_font_size
== s2
->i_font_size
) &&
1425 ( s1
->i_font_color
== s2
->i_font_color
) &&
1426 ( s1
->b_italic
== s2
->b_italic
) &&
1427 ( s1
->b_bold
== s2
->b_bold
) &&
1428 ( s1
->b_underline
== s2
->b_underline
) &&
1429 ( !strcmp( s1
->psz_fontname
, s2
->psz_fontname
)))
1436 static int PushFont( font_stack_t
**p_font
, const char *psz_name
, int i_size
,
1437 uint32_t i_color
, uint32_t i_karaoke_bg_color
)
1439 font_stack_t
*p_new
;
1442 return VLC_EGENERIC
;
1444 p_new
= malloc( sizeof( font_stack_t
) );
1448 p_new
->p_next
= NULL
;
1451 p_new
->psz_name
= strdup( psz_name
);
1453 p_new
->psz_name
= NULL
;
1455 p_new
->i_size
= i_size
;
1456 p_new
->i_color
= i_color
;
1457 p_new
->i_karaoke_bg_color
= i_karaoke_bg_color
;
1465 font_stack_t
*p_last
;
1467 for( p_last
= *p_font
;
1469 p_last
= p_last
->p_next
)
1472 p_last
->p_next
= p_new
;
1477 static int PopFont( font_stack_t
**p_font
)
1479 font_stack_t
*p_last
, *p_next_to_last
;
1481 if( !p_font
|| !*p_font
)
1482 return VLC_EGENERIC
;
1484 p_next_to_last
= NULL
;
1485 for( p_last
= *p_font
;
1487 p_last
= p_last
->p_next
)
1489 p_next_to_last
= p_last
;
1492 if( p_next_to_last
)
1493 p_next_to_last
->p_next
= NULL
;
1497 free( p_last
->psz_name
);
1503 static int PeekFont( font_stack_t
**p_font
, char **psz_name
, int *i_size
,
1504 uint32_t *i_color
, uint32_t *i_karaoke_bg_color
)
1506 font_stack_t
*p_last
;
1508 if( !p_font
|| !*p_font
)
1509 return VLC_EGENERIC
;
1511 for( p_last
=*p_font
;
1513 p_last
=p_last
->p_next
)
1516 *psz_name
= p_last
->psz_name
;
1517 *i_size
= p_last
->i_size
;
1518 *i_color
= p_last
->i_color
;
1519 *i_karaoke_bg_color
= p_last
->i_karaoke_bg_color
;
1524 static void IconvText( filter_t
*p_filter
, const char *psz_string
,
1525 uint32_t *i_string_length
, uint32_t **ppsz_unicode
)
1527 vlc_iconv_t iconv_handle
= (vlc_iconv_t
)(-1);
1529 /* If memory hasn't been allocated for our output string, allocate it here
1530 * - the calling function must now be responsible for freeing it.
1532 if( !*ppsz_unicode
)
1533 *ppsz_unicode
= (uint32_t *)
1534 malloc( (strlen( psz_string
) + 1) * sizeof( uint32_t ));
1536 /* We don't need to handle a NULL pointer in *ppsz_unicode
1537 * if we are instead testing for a non NULL value like we are here */
1541 #if defined(WORDS_BIGENDIAN)
1542 iconv_handle
= vlc_iconv_open( "UCS-4BE", "UTF-8" );
1544 iconv_handle
= vlc_iconv_open( "UCS-4LE", "UTF-8" );
1546 if( iconv_handle
!= (vlc_iconv_t
)-1 )
1548 char *p_in_buffer
, *p_out_buffer
;
1549 size_t i_in_bytes
, i_out_bytes
, i_out_bytes_left
, i_ret
;
1550 i_in_bytes
= strlen( psz_string
);
1551 i_out_bytes
= i_in_bytes
* sizeof( uint32_t );
1552 i_out_bytes_left
= i_out_bytes
;
1553 p_in_buffer
= (char *) psz_string
;
1554 p_out_buffer
= (char *) *ppsz_unicode
;
1555 i_ret
= vlc_iconv( iconv_handle
, (const char**)&p_in_buffer
,
1556 &i_in_bytes
, &p_out_buffer
, &i_out_bytes_left
);
1558 vlc_iconv_close( iconv_handle
);
1562 msg_Warn( p_filter
, "failed to convert string to unicode (%m), "
1563 "bytes left %u", (unsigned)i_in_bytes
);
1567 *(uint32_t*)p_out_buffer
= 0;
1569 (i_out_bytes
- i_out_bytes_left
) / sizeof(uint32_t);
1574 msg_Warn( p_filter
, "unable to do conversion" );
1579 static ft_style_t
*GetStyleFromFontStack( filter_sys_t
*p_sys
,
1580 font_stack_t
**p_fonts
, bool b_bold
, bool b_italic
,
1583 ft_style_t
*p_style
= NULL
;
1585 char *psz_fontname
= NULL
;
1586 uint32_t i_font_color
= p_sys
->i_font_color
& 0x00ffffff;
1587 uint32_t i_karaoke_bg_color
= i_font_color
;
1588 int i_font_size
= p_sys
->i_font_size
;
1590 if( VLC_SUCCESS
== PeekFont( p_fonts
, &psz_fontname
, &i_font_size
,
1591 &i_font_color
, &i_karaoke_bg_color
))
1593 p_style
= CreateStyle( psz_fontname
, i_font_size
, i_font_color
,
1594 i_karaoke_bg_color
, b_bold
, b_italic
, b_uline
);
1599 static int RenderTag( filter_t
*p_filter
, FT_Face p_face
, int i_font_color
,
1600 bool b_uline
, int i_karaoke_bgcolor
,
1601 line_desc_t
*p_line
, uint32_t *psz_unicode
,
1602 int *pi_pen_x
, int i_pen_y
, int *pi_start
,
1603 FT_Vector
*p_result
)
1608 bool b_first_on_line
= true;
1611 int i_pen_x_start
= *pi_pen_x
;
1613 uint32_t *psz_unicode_start
= psz_unicode
;
1615 line
.xMin
= line
.xMax
= line
.yMin
= line
.yMax
= 0;
1617 /* Account for part of line already in position */
1618 for( i
=0; i
<*pi_start
; i
++ )
1622 FT_Glyph_Get_CBox( (FT_Glyph
) p_line
->pp_glyphs
[ i
],
1623 ft_glyph_bbox_pixels
, &glyph_size
);
1625 line
.xMax
= p_line
->p_glyph_pos
[ i
].x
+ glyph_size
.xMax
-
1626 glyph_size
.xMin
+ p_line
->pp_glyphs
[ i
]->left
;
1627 line
.yMax
= __MAX( line
.yMax
, glyph_size
.yMax
);
1628 line
.yMin
= __MIN( line
.yMin
, glyph_size
.yMin
);
1634 b_first_on_line
= false;
1636 while( *psz_unicode
&& ( *psz_unicode
!= '\n' ) )
1642 int i_glyph_index
= FT_Get_Char_Index( p_face
, *psz_unicode
++ );
1643 if( FT_HAS_KERNING( p_face
) && i_glyph_index
1647 FT_Get_Kerning( p_face
, i_previous
, i_glyph_index
,
1648 ft_kerning_default
, &delta
);
1649 *pi_pen_x
+= delta
.x
>> 6;
1651 p_line
->p_glyph_pos
[ i
].x
= *pi_pen_x
;
1652 p_line
->p_glyph_pos
[ i
].y
= i_pen_y
;
1654 i_error
= FT_Load_Glyph( p_face
, i_glyph_index
, FT_LOAD_DEFAULT
);
1658 "unable to render text FT_Load_Glyph returned %d", i_error
);
1659 p_line
->pp_glyphs
[ i
] = NULL
;
1660 return VLC_EGENERIC
;
1662 i_error
= FT_Get_Glyph( p_face
->glyph
, &tmp_glyph
);
1666 "unable to render text FT_Get_Glyph returned %d", i_error
);
1667 p_line
->pp_glyphs
[ i
] = NULL
;
1668 return VLC_EGENERIC
;
1670 FT_Glyph_Get_CBox( tmp_glyph
, ft_glyph_bbox_pixels
, &glyph_size
);
1671 i_error
= FT_Glyph_To_Bitmap( &tmp_glyph
, FT_RENDER_MODE_NORMAL
, 0, 1);
1674 FT_Done_Glyph( tmp_glyph
);
1679 float aOffset
= FT_FLOOR(FT_MulFix(p_face
->underline_position
,
1680 p_face
->size
->metrics
.y_scale
));
1681 float aSize
= FT_CEIL(FT_MulFix(p_face
->underline_thickness
,
1682 p_face
->size
->metrics
.y_scale
));
1684 p_line
->pi_underline_offset
[ i
] =
1685 ( aOffset
< 0 ) ? -aOffset
: aOffset
;
1686 p_line
->pi_underline_thickness
[ i
] =
1687 ( aSize
< 0 ) ? -aSize
: aSize
;
1689 p_line
->pp_glyphs
[ i
] = (FT_BitmapGlyph
)tmp_glyph
;
1690 p_line
->p_fg_rgb
[ i
] = i_font_color
& 0x00ffffff;
1691 p_line
->p_bg_rgb
[ i
] = i_karaoke_bgcolor
& 0x00ffffff;
1692 p_line
->p_fg_bg_ratio
[ i
] = 0x00;
1694 line
.xMax
= p_line
->p_glyph_pos
[i
].x
+ glyph_size
.xMax
-
1695 glyph_size
.xMin
+ ((FT_BitmapGlyph
)tmp_glyph
)->left
;
1696 if( line
.xMax
> (int)p_filter
->fmt_out
.video
.i_visible_width
- 20 )
1698 while( --i
> *pi_start
)
1700 FT_Done_Glyph( (FT_Glyph
)p_line
->pp_glyphs
[ i
] );
1703 while( psz_unicode
> psz_unicode_start
&& *psz_unicode
!= ' ' )
1707 if( psz_unicode
== psz_unicode_start
)
1709 if( b_first_on_line
)
1711 msg_Warn( p_filter
, "unbreakable string" );
1712 p_line
->pp_glyphs
[ i
] = NULL
;
1713 return VLC_EGENERIC
;
1715 *pi_pen_x
= i_pen_x_start
;
1717 p_line
->i_width
= line
.xMax
;
1718 p_line
->i_height
= __MAX( p_line
->i_height
,
1719 p_face
->size
->metrics
.height
>> 6 );
1720 p_line
->pp_glyphs
[ i
] = NULL
;
1722 p_result
->x
= __MAX( p_result
->x
, line
.xMax
);
1723 p_result
->y
= __MAX( p_result
->y
, __MAX( p_line
->i_height
,
1724 i_yMax
- i_yMin
) );
1731 *psz_unicode
= '\n';
1733 psz_unicode
= psz_unicode_start
;
1734 *pi_pen_x
= i_pen_x_start
;
1742 line
.yMax
= __MAX( line
.yMax
, glyph_size
.yMax
);
1743 line
.yMin
= __MIN( line
.yMin
, glyph_size
.yMin
);
1745 i_previous
= i_glyph_index
;
1746 *pi_pen_x
+= p_face
->glyph
->advance
.x
>> 6;
1749 p_line
->i_width
= line
.xMax
;
1750 p_line
->i_height
= __MAX( p_line
->i_height
,
1751 p_face
->size
->metrics
.height
>> 6 );
1752 p_line
->pp_glyphs
[ i
] = NULL
;
1754 p_result
->x
= __MAX( p_result
->x
, line
.xMax
);
1755 p_result
->y
= __MAX( p_result
->y
, __MAX( p_line
->i_height
,
1756 line
.yMax
- line
.yMin
) );
1760 /* Get rid of any text processed - if necessary repositioning
1761 * at the start of a new line of text
1765 *psz_unicode_start
= '\0';
1767 else if( psz_unicode
> psz_unicode_start
)
1769 for( i
=0; psz_unicode
[ i
]; i
++ )
1770 psz_unicode_start
[ i
] = psz_unicode
[ i
];
1771 psz_unicode_start
[ i
] = '\0';
1777 static int HandleFontAttributes( xml_reader_t
*p_xml_reader
,
1778 font_stack_t
**p_fonts
, int i_scale
)
1781 char *psz_fontname
= NULL
;
1782 uint32_t i_font_color
= 0xffffff;
1783 int i_font_alpha
= 0;
1784 uint32_t i_karaoke_bg_color
= 0x00ffffff;
1785 int i_font_size
= 24;
1787 /* Default all attributes to the top font in the stack -- in case not
1788 * all attributes are specified in the sub-font
1790 if( VLC_SUCCESS
== PeekFont( p_fonts
,
1794 &i_karaoke_bg_color
))
1796 psz_fontname
= strdup( psz_fontname
);
1797 i_font_size
= i_font_size
* 1000 / i_scale
;
1799 i_font_alpha
= (i_font_color
>> 24) & 0xff;
1800 i_font_color
&= 0x00ffffff;
1802 while ( xml_ReaderNextAttr( p_xml_reader
) == VLC_SUCCESS
)
1804 char *psz_name
= xml_ReaderName( p_xml_reader
);
1805 char *psz_value
= xml_ReaderValue( p_xml_reader
);
1807 if( psz_name
&& psz_value
)
1809 if( !strcasecmp( "face", psz_name
) )
1811 free( psz_fontname
);
1812 psz_fontname
= strdup( psz_value
);
1814 else if( !strcasecmp( "size", psz_name
) )
1816 if( ( *psz_value
== '+' ) || ( *psz_value
== '-' ) )
1818 int i_value
= atoi( psz_value
);
1820 if( ( i_value
>= -5 ) && ( i_value
<= 5 ) )
1821 i_font_size
+= ( i_value
* i_font_size
) / 10;
1822 else if( i_value
< -5 )
1823 i_font_size
= - i_value
;
1824 else if( i_value
> 5 )
1825 i_font_size
= i_value
;
1828 i_font_size
= atoi( psz_value
);
1830 else if( !strcasecmp( "color", psz_name
) &&
1831 ( psz_value
[0] == '#' ) )
1833 i_font_color
= strtol( psz_value
+ 1, NULL
, 16 );
1834 i_font_color
&= 0x00ffffff;
1836 else if( !strcasecmp( "alpha", psz_name
) &&
1837 ( psz_value
[0] == '#' ) )
1839 i_font_alpha
= strtol( psz_value
+ 1, NULL
, 16 );
1840 i_font_alpha
&= 0xff;
1846 rv
= PushFont( p_fonts
,
1848 i_font_size
* i_scale
/ 1000,
1849 (i_font_color
& 0xffffff) | ((i_font_alpha
& 0xff) << 24),
1850 i_karaoke_bg_color
);
1852 free( psz_fontname
);
1857 static void SetupLine( filter_t
*p_filter
, const char *psz_text_in
,
1858 uint32_t **psz_text_out
, uint32_t *pi_runs
,
1859 uint32_t **ppi_run_lengths
, ft_style_t
***ppp_styles
,
1860 ft_style_t
*p_style
)
1862 uint32_t i_string_length
= 0;
1864 IconvText( p_filter
, psz_text_in
, &i_string_length
, psz_text_out
);
1865 *psz_text_out
+= i_string_length
;
1867 if( ppp_styles
&& ppi_run_lengths
)
1873 *ppp_styles
= (ft_style_t
**)
1874 realloc( *ppp_styles
, *pi_runs
* sizeof( ft_style_t
* ) );
1876 else if( *pi_runs
== 1 )
1878 *ppp_styles
= (ft_style_t
**)
1879 malloc( *pi_runs
* sizeof( ft_style_t
* ) );
1882 /* We have just malloc'ed this memory successfully -
1883 * *pi_runs HAS to be within the memory area of *ppp_styles */
1886 (*ppp_styles
)[ *pi_runs
- 1 ] = p_style
;
1890 if( *ppi_run_lengths
)
1892 *ppi_run_lengths
= (uint32_t *)
1893 realloc( *ppi_run_lengths
, *pi_runs
* sizeof( uint32_t ) );
1895 else if( *pi_runs
== 1 )
1897 *ppi_run_lengths
= (uint32_t *)
1898 malloc( *pi_runs
* sizeof( uint32_t ) );
1901 /* same remarks here */
1902 if( *ppi_run_lengths
)
1904 (*ppi_run_lengths
)[ *pi_runs
- 1 ] = i_string_length
;
1907 /* If we couldn't use the p_style argument due to memory allocation
1908 * problems above, release it here.
1910 if( p_style
) DeleteStyle( p_style
);
1913 static void SetKaraokeLen( uint32_t i_runs
, uint32_t *pi_run_lengths
,
1914 uint32_t i_k_runs
, uint32_t *pi_k_run_lengths
)
1916 /* Karaoke tags _PRECEDE_ the text they specify a duration
1917 * for, therefore we are working out the length for the
1918 * previous tag, and first time through we have nothing
1920 if( pi_k_run_lengths
)
1925 /* Work out how many characters are presently in the string
1927 for( i
= 0; i
< i_runs
; i
++ )
1928 i_chars
+= pi_run_lengths
[ i
];
1930 /* Subtract away those we've already allocated to other
1933 for( i
= 0; i
< i_k_runs
; i
++ )
1934 i_chars
-= pi_k_run_lengths
[ i
];
1936 pi_k_run_lengths
[ i_k_runs
- 1 ] = i_chars
;
1940 static void SetupKaraoke( xml_reader_t
*p_xml_reader
, uint32_t *pi_k_runs
,
1941 uint32_t **ppi_k_run_lengths
,
1942 uint32_t **ppi_k_durations
)
1944 while ( xml_ReaderNextAttr( p_xml_reader
) == VLC_SUCCESS
)
1946 char *psz_name
= xml_ReaderName( p_xml_reader
);
1947 char *psz_value
= xml_ReaderValue( p_xml_reader
);
1949 if( psz_name
&& psz_value
&&
1950 !strcasecmp( "t", psz_name
) )
1952 if( ppi_k_durations
&& ppi_k_run_lengths
)
1956 if( *ppi_k_durations
)
1958 *ppi_k_durations
= (uint32_t *)
1959 realloc( *ppi_k_durations
,
1960 *pi_k_runs
* sizeof( uint32_t ) );
1962 else if( *pi_k_runs
== 1 )
1964 *ppi_k_durations
= (uint32_t *)
1965 malloc( *pi_k_runs
* sizeof( uint32_t ) );
1968 if( *ppi_k_run_lengths
)
1970 *ppi_k_run_lengths
= (uint32_t *)
1971 realloc( *ppi_k_run_lengths
,
1972 *pi_k_runs
* sizeof( uint32_t ) );
1974 else if( *pi_k_runs
== 1 )
1976 *ppi_k_run_lengths
= (uint32_t *)
1977 malloc( *pi_k_runs
* sizeof( uint32_t ) );
1979 if( *ppi_k_durations
)
1980 (*ppi_k_durations
)[ *pi_k_runs
- 1 ] = atoi( psz_value
);
1982 if( *ppi_k_run_lengths
)
1983 (*ppi_k_run_lengths
)[ *pi_k_runs
- 1 ] = 0;
1991 static int ProcessNodes( filter_t
*p_filter
,
1992 xml_reader_t
*p_xml_reader
,
1993 text_style_t
*p_font_style
,
1998 uint32_t **ppi_run_lengths
,
1999 ft_style_t
***ppp_styles
,
2002 uint32_t *pi_k_runs
,
2003 uint32_t **ppi_k_run_lengths
,
2004 uint32_t **ppi_k_durations
)
2006 int rv
= VLC_SUCCESS
;
2007 filter_sys_t
*p_sys
= p_filter
->p_sys
;
2008 uint32_t *psz_text_orig
= psz_text
;
2009 font_stack_t
*p_fonts
= NULL
;
2013 char *psz_node
= NULL
;
2015 bool b_italic
= false;
2016 bool b_bold
= false;
2017 bool b_uline
= false;
2019 if( VLC_SUCCESS
== var_Get( p_filter
, "scale", &val
))
2020 i_scale
= val
.i_int
;
2024 rv
= PushFont( &p_fonts
,
2025 p_font_style
->psz_fontname
,
2026 p_font_style
->i_font_size
* i_scale
/ 1000,
2027 (p_font_style
->i_font_color
& 0xffffff) |
2028 ((p_font_style
->i_font_alpha
& 0xff) << 24),
2029 (p_font_style
->i_karaoke_background_color
& 0xffffff) |
2030 ((p_font_style
->i_karaoke_background_alpha
& 0xff) << 24));
2032 if( p_font_style
->i_style_flags
& STYLE_BOLD
)
2034 if( p_font_style
->i_style_flags
& STYLE_ITALIC
)
2036 if( p_font_style
->i_style_flags
& STYLE_UNDERLINE
)
2041 rv
= PushFont( &p_fonts
,
2047 if( rv
!= VLC_SUCCESS
)
2050 while ( ( xml_ReaderRead( p_xml_reader
) == 1 ) )
2052 switch ( xml_ReaderNodeType( p_xml_reader
) )
2054 case XML_READER_NONE
:
2056 case XML_READER_ENDELEM
:
2057 psz_node
= xml_ReaderName( p_xml_reader
);
2061 if( !strcasecmp( "font", psz_node
) )
2062 PopFont( &p_fonts
);
2063 else if( !strcasecmp( "b", psz_node
) )
2065 else if( !strcasecmp( "i", psz_node
) )
2067 else if( !strcasecmp( "u", psz_node
) )
2073 case XML_READER_STARTELEM
:
2074 psz_node
= xml_ReaderName( p_xml_reader
);
2077 if( !strcasecmp( "font", psz_node
) )
2078 rv
= HandleFontAttributes( p_xml_reader
, &p_fonts
, i_scale
);
2079 else if( !strcasecmp( "b", psz_node
) )
2081 else if( !strcasecmp( "i", psz_node
) )
2083 else if( !strcasecmp( "u", psz_node
) )
2085 else if( !strcasecmp( "br", psz_node
) )
2087 SetupLine( p_filter
, "\n", &psz_text
,
2088 pi_runs
, ppi_run_lengths
, ppp_styles
,
2089 GetStyleFromFontStack( p_sys
,
2095 else if( !strcasecmp( "k", psz_node
) )
2097 /* Only valid in karaoke */
2100 if( *pi_k_runs
> 0 )
2102 SetKaraokeLen( *pi_runs
, *ppi_run_lengths
,
2103 *pi_k_runs
, *ppi_k_run_lengths
);
2105 SetupKaraoke( p_xml_reader
, pi_k_runs
,
2106 ppi_k_run_lengths
, ppi_k_durations
);
2113 case XML_READER_TEXT
:
2114 psz_node
= xml_ReaderValue( p_xml_reader
);
2117 /* Turn any multiple-whitespaces into single spaces */
2118 char *s
= strpbrk( psz_node
, "\t\r\n " );
2121 int i_whitespace
= strspn( s
, "\t\r\n " );
2123 if( i_whitespace
> 1 )
2126 strlen( s
) - i_whitespace
+ 1 );
2129 s
= strpbrk( s
, "\t\r\n " );
2131 SetupLine( p_filter
, psz_node
, &psz_text
,
2132 pi_runs
, ppi_run_lengths
, ppp_styles
,
2133 GetStyleFromFontStack( p_sys
,
2142 if( rv
!= VLC_SUCCESS
)
2144 psz_text
= psz_text_orig
;
2150 SetKaraokeLen( *pi_runs
, *ppi_run_lengths
,
2151 *pi_k_runs
, *ppi_k_run_lengths
);
2154 *pi_len
= psz_text
- psz_text_orig
;
2156 while( VLC_SUCCESS
== PopFont( &p_fonts
) );
2161 static int CheckForEmbeddedFont( filter_sys_t
*p_sys
, FT_Face
*pp_face
, ft_style_t
*p_style
)
2165 for( k
=0; k
< p_sys
->i_font_attachments
; k
++ )
2167 input_attachment_t
*p_attach
= p_sys
->pp_font_attachments
[k
];
2169 FT_Face p_face
= NULL
;
2171 while( 0 == FT_New_Memory_Face( p_sys
->p_library
,
2179 bool match
= !strcasecmp( p_face
->family_name
,
2180 p_style
->psz_fontname
);
2182 if( p_face
->style_flags
& FT_STYLE_FLAG_BOLD
)
2183 match
= match
&& p_style
->b_bold
;
2185 match
= match
&& !p_style
->b_bold
;
2187 if( p_face
->style_flags
& FT_STYLE_FLAG_ITALIC
)
2188 match
= match
&& p_style
->b_italic
;
2190 match
= match
&& !p_style
->b_italic
;
2198 FT_Done_Face( p_face
);
2203 return VLC_EGENERIC
;
2206 static int BuildDone( vlc_object_t
*p_this
, const char *psz_var
,
2207 vlc_value_t oldval
, vlc_value_t newval
, void *param
)
2212 ((filter_sys_t
*)param
)->b_fontconfig_ok
= newval
.b_bool
;
2213 assert( newval
.b_bool
);
2217 static int ProcessLines( filter_t
*p_filter
,
2222 uint32_t *pi_run_lengths
,
2223 ft_style_t
**pp_styles
,
2224 line_desc_t
**pp_lines
,
2226 FT_Vector
*p_result
,
2230 uint32_t *pi_k_run_lengths
,
2231 uint32_t *pi_k_durations
)
2233 filter_sys_t
*p_sys
= p_filter
->p_sys
;
2234 ft_style_t
**pp_char_styles
;
2235 int *p_new_positions
= NULL
;
2236 int8_t *p_levels
= NULL
;
2237 uint8_t *pi_karaoke_bar
= NULL
;
2241 /* Assign each character in the text string its style explicitly, so that
2242 * after the characters have been shuffled around by Fribidi, we can re-apply
2243 * the styles, and to simplify the calculation of runs within a line.
2245 pp_char_styles
= (ft_style_t
**) malloc( i_len
* sizeof( ft_style_t
* ));
2246 if( !pp_char_styles
)
2251 pi_karaoke_bar
= (uint8_t *) malloc( i_len
* sizeof( uint8_t ));
2252 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2253 * we just won't be able to display the progress bar; at least we'll
2259 for( j
= 0; j
< i_runs
; j
++ )
2260 for( k
= 0; k
< pi_run_lengths
[ j
]; k
++ )
2261 pp_char_styles
[ i
++ ] = pp_styles
[ j
];
2263 #if defined(HAVE_FRIBIDI)
2265 ft_style_t
**pp_char_styles_new
;
2266 int *p_old_positions
;
2267 uint32_t *p_fribidi_string
;
2268 int start_pos
, pos
= 0;
2270 pp_char_styles_new
= (ft_style_t
**)
2271 malloc( i_len
* sizeof( ft_style_t
* ));
2273 p_fribidi_string
= (uint32_t *)
2274 malloc( (i_len
+ 1) * sizeof(uint32_t) );
2275 p_old_positions
= (int *)
2276 malloc( (i_len
+ 1) * sizeof( int ) );
2277 p_new_positions
= (int *)
2278 malloc( (i_len
+ 1) * sizeof( int ) );
2279 p_levels
= (int8_t *)
2280 malloc( (i_len
+ 1) * sizeof( int8_t ) );
2282 if( ! pp_char_styles_new
||
2283 ! p_fribidi_string
||
2284 ! p_old_positions
||
2285 ! p_new_positions
||
2288 msg_Err( p_filter
, "out of memory" );
2290 free( p_old_positions
);
2291 free( p_new_positions
);
2292 free( p_fribidi_string
);
2293 free( pp_char_styles_new
);
2294 free( pi_karaoke_bar
);
2296 free( pp_char_styles
);
2300 /* Do bidi conversion line-by-line */
2303 while(pos
< i_len
) {
2304 if (psz_text
[pos
] != '\n')
2306 p_fribidi_string
[pos
] = psz_text
[pos
];
2307 pp_char_styles_new
[pos
] = pp_char_styles
[pos
];
2308 p_new_positions
[pos
] = pos
;
2313 while(pos
< i_len
) {
2314 if (psz_text
[pos
] == '\n')
2318 if (pos
> start_pos
)
2320 FriBidiCharType base_dir
= FRIBIDI_TYPE_LTR
;
2321 fribidi_log2vis((FriBidiChar
*)psz_text
+ start_pos
,
2322 pos
- start_pos
, &base_dir
,
2323 (FriBidiChar
*)p_fribidi_string
+ start_pos
,
2324 p_new_positions
+ start_pos
,
2326 p_levels
+ start_pos
);
2327 for( j
= (uint32_t) start_pos
; j
< (uint32_t) pos
; j
++ )
2329 pp_char_styles_new
[ j
] = pp_char_styles
[ start_pos
+
2330 p_old_positions
[ j
- start_pos
] ];
2331 p_new_positions
[ j
] += start_pos
;
2335 free( p_old_positions
);
2336 free( pp_char_styles
);
2337 pp_char_styles
= pp_char_styles_new
;
2338 psz_text
= p_fribidi_string
;
2339 p_fribidi_string
[ i_len
] = 0;
2342 /* Work out the karaoke */
2343 if( pi_karaoke_bar
)
2345 int64_t i_last_duration
= 0;
2346 int64_t i_duration
= 0;
2347 int64_t i_start_pos
= 0;
2348 int64_t i_elapsed
= var_GetTime( p_filter
, "spu-elapsed" ) / 1000;
2350 for( k
= 0; k
< i_k_runs
; k
++ )
2352 double fraction
= 0.0;
2354 i_duration
+= pi_k_durations
[ k
];
2356 if( i_duration
< i_elapsed
)
2358 /* Completely finished this run-length -
2359 * let it render normally */
2363 else if( i_elapsed
< i_last_duration
)
2365 /* Haven't got up to this segment yet -
2366 * render it completely in karaoke BG mode */
2372 /* Partway through this run */
2374 fraction
= (double)(i_elapsed
- i_last_duration
) /
2375 (double)pi_k_durations
[ k
];
2377 for( i
= 0; i
< pi_k_run_lengths
[ k
]; i
++ )
2379 double shade
= pi_k_run_lengths
[ k
] * fraction
;
2381 if( p_new_positions
)
2382 j
= p_new_positions
[ i_start_pos
+ i
];
2384 j
= i_start_pos
+ i
;
2386 if( i
< (uint32_t)shade
)
2387 pi_karaoke_bar
[ j
] = 0xff;
2388 else if( (double)i
> shade
)
2389 pi_karaoke_bar
[ j
] = 0x00;
2392 shade
-= (int)shade
;
2393 pi_karaoke_bar
[ j
] = ((int)(shade
* 128.0) & 0x7f) |
2394 ((p_levels
? (p_levels
[ j
] % 2) : 0 ) << 7);
2398 i_last_duration
= i_duration
;
2399 i_start_pos
+= pi_k_run_lengths
[ k
];
2403 free( p_new_positions
);
2405 FT_Vector tmp_result
;
2407 line_desc_t
*p_line
= NULL
;
2408 line_desc_t
*p_prev
= NULL
;
2414 p_result
->x
= p_result
->y
= 0;
2415 tmp_result
.x
= tmp_result
.y
= 0;
2418 for( k
= 0; k
<= (uint32_t) i_len
; k
++ )
2420 if( ( k
== (uint32_t) i_len
) ||
2422 !StyleEquals( pp_char_styles
[ k
], pp_char_styles
[ k
- 1] ) ) )
2424 ft_style_t
*p_style
= pp_char_styles
[ k
- 1 ];
2426 /* End of the current style run */
2427 FT_Face p_face
= NULL
;
2430 /* Look for a match amongst our attachments first */
2431 CheckForEmbeddedFont( p_sys
, &p_face
, p_style
);
2433 if( ! p_face
&& p_sys
->b_fontconfig_ok
)
2436 vlc_mutex_lock( &p_sys
->fontconfig_lock
);
2438 psz_fontfile
= FontConfig_Select( p_sys
->p_fontconfig
,
2439 p_style
->psz_fontname
,
2443 vlc_mutex_unlock( &p_sys
->fontconfig_lock
);
2445 if( psz_fontfile
&& ! *psz_fontfile
)
2447 msg_Warn( p_filter
, "Fontconfig was unable to find a font: \"%s\" %s"
2448 " so using default font", p_style
->psz_fontname
,
2449 ((p_style
->b_bold
&& p_style
->b_italic
) ? "(Bold,Italic)" :
2450 (p_style
->b_bold
? "(Bold)" :
2451 (p_style
->b_italic
? "(Italic)" : ""))) );
2452 free( psz_fontfile
);
2453 psz_fontfile
= NULL
;
2458 if( FT_New_Face( p_sys
->p_library
,
2459 psz_fontfile
, i_idx
, &p_face
) )
2461 free( psz_fontfile
);
2462 free( pp_char_styles
);
2463 #if defined(HAVE_FRIBIDI)
2466 free( pi_karaoke_bar
);
2467 return VLC_EGENERIC
;
2469 free( psz_fontfile
);
2473 FT_Select_Charmap( p_face
, ft_encoding_unicode
) )
2475 /* We've loaded a font face which is unhelpful for actually
2476 * rendering text - fallback to the default one.
2478 FT_Done_Face( p_face
);
2482 if( FT_Select_Charmap( p_face
? p_face
: p_sys
->p_face
,
2483 ft_encoding_unicode
) ||
2484 FT_Set_Pixel_Sizes( p_face
? p_face
: p_sys
->p_face
, 0,
2485 p_style
->i_font_size
) )
2487 if( p_face
) FT_Done_Face( p_face
);
2488 free( pp_char_styles
);
2489 #if defined(HAVE_FRIBIDI)
2492 free( pi_karaoke_bar
);
2493 return VLC_EGENERIC
;
2495 p_sys
->i_use_kerning
=
2496 FT_HAS_KERNING( ( p_face
? p_face
: p_sys
->p_face
) );
2499 uint32_t *psz_unicode
= (uint32_t *)
2500 malloc( (k
- i_prev
+ 1) * sizeof( uint32_t ));
2503 msg_Err( p_filter
, "out of memory" );
2504 if( p_face
) FT_Done_Face( p_face
);
2505 free( pp_char_styles
);
2506 free( psz_unicode
);
2507 #if defined(HAVE_FRIBIDI)
2510 free( pi_karaoke_bar
);
2513 memcpy( psz_unicode
, psz_text
+ i_prev
,
2514 sizeof( uint32_t ) * ( k
- i_prev
) );
2515 psz_unicode
[ k
- i_prev
] = 0;
2516 while( *psz_unicode
)
2520 if( !(p_line
= NewLine( i_len
- i_prev
)) )
2522 msg_Err( p_filter
, "out of memory" );
2523 if( p_face
) FT_Done_Face( p_face
);
2524 free( pp_char_styles
);
2525 free( psz_unicode
);
2526 #if defined(HAVE_FRIBIDI)
2529 free( pi_karaoke_bar
);
2532 /* New Color mode only works in YUVA rendering mode --
2533 * (RGB mode has palette constraints on it). We therefore
2534 * need to populate the legacy colour fields also.
2536 p_line
->b_new_color_mode
= true;
2537 p_line
->i_alpha
= ( p_style
->i_font_color
& 0xff000000 ) >> 24;
2538 p_line
->i_red
= ( p_style
->i_font_color
& 0x00ff0000 ) >> 16;
2539 p_line
->i_green
= ( p_style
->i_font_color
& 0x0000ff00 ) >> 8;
2540 p_line
->i_blue
= ( p_style
->i_font_color
& 0x000000ff );
2541 p_line
->p_next
= NULL
;
2543 i_pen_y
+= tmp_result
.y
;
2547 if( p_prev
) p_prev
->p_next
= p_line
;
2548 else *pp_lines
= p_line
;
2551 if( RenderTag( p_filter
, p_face
? p_face
: p_sys
->p_face
,
2552 p_style
->i_font_color
, p_style
->b_underline
,
2553 p_style
->i_karaoke_bg_color
,
2554 p_line
, psz_unicode
, &i_pen_x
, i_pen_y
, &i_posn
,
2555 &tmp_result
) != VLC_SUCCESS
)
2557 if( p_face
) FT_Done_Face( p_face
);
2558 free( pp_char_styles
);
2559 free( psz_unicode
);
2560 #if defined(HAVE_FRIBIDI)
2563 free( pi_karaoke_bar
);
2564 return VLC_EGENERIC
;
2569 p_result
->x
= __MAX( p_result
->x
, tmp_result
.x
);
2570 p_result
->y
+= tmp_result
.y
;
2575 if( *psz_unicode
== '\n')
2579 for( c_ptr
= psz_unicode
; *c_ptr
; c_ptr
++ )
2581 *c_ptr
= *(c_ptr
+1);
2586 free( psz_unicode
);
2587 if( p_face
) FT_Done_Face( p_face
);
2591 free( pp_char_styles
);
2592 #if defined(HAVE_FRIBIDI)
2597 p_result
->x
= __MAX( p_result
->x
, tmp_result
.x
);
2598 p_result
->y
+= tmp_result
.y
;
2601 if( pi_karaoke_bar
)
2604 for( p_line
= *pp_lines
; p_line
; p_line
=p_line
->p_next
)
2606 for( k
= 0; p_line
->pp_glyphs
[ k
]; k
++, i
++ )
2608 if( (pi_karaoke_bar
[ i
] & 0x7f) == 0x7f)
2612 else if( (pi_karaoke_bar
[ i
] & 0x7f) == 0x00)
2614 /* 100% BG colour will render faster if we
2615 * instead make it 100% FG colour, so leave
2616 * the ratio alone and copy the value across
2618 p_line
->p_fg_rgb
[ k
] = p_line
->p_bg_rgb
[ k
];
2622 if( pi_karaoke_bar
[ i
] & 0x80 )
2624 /* Swap Left and Right sides over for Right aligned
2625 * language text (eg. Arabic, Hebrew)
2627 uint32_t i_tmp
= p_line
->p_fg_rgb
[ k
];
2629 p_line
->p_fg_rgb
[ k
] = p_line
->p_bg_rgb
[ k
];
2630 p_line
->p_bg_rgb
[ k
] = i_tmp
;
2632 p_line
->p_fg_bg_ratio
[ k
] = (pi_karaoke_bar
[ i
] & 0x7f);
2635 /* Jump over the '\n' at the line-end */
2638 free( pi_karaoke_bar
);
2644 static int RenderHtml( filter_t
*p_filter
, subpicture_region_t
*p_region_out
,
2645 subpicture_region_t
*p_region_in
)
2647 int rv
= VLC_SUCCESS
;
2648 stream_t
*p_sub
= NULL
;
2649 xml_t
*p_xml
= NULL
;
2650 xml_reader_t
*p_xml_reader
= NULL
;
2652 if( !p_region_in
|| !p_region_in
->psz_html
)
2653 return VLC_EGENERIC
;
2655 /* Reset the default fontsize in case screen metrics have changed */
2656 p_filter
->p_sys
->i_font_size
= GetFontSize( p_filter
);
2658 p_sub
= stream_MemoryNew( VLC_OBJECT(p_filter
),
2659 (uint8_t *) p_region_in
->psz_html
,
2660 strlen( p_region_in
->psz_html
),
2664 p_xml
= xml_Create( p_filter
);
2667 bool b_karaoke
= false;
2669 p_xml_reader
= xml_ReaderCreate( p_xml
, p_sub
);
2672 /* Look for Root Node */
2673 if( xml_ReaderRead( p_xml_reader
) == 1 )
2675 char *psz_node
= xml_ReaderName( p_xml_reader
);
2677 if( !strcasecmp( "karaoke", psz_node
) )
2679 /* We're going to have to render the text a number
2680 * of times to show the progress marker on the text.
2682 var_SetBool( p_filter
, "text-rerender", true );
2685 else if( !strcasecmp( "text", psz_node
) )
2691 /* Only text and karaoke tags are supported */
2692 msg_Dbg( p_filter
, "Unsupported top-level tag '%s' ignored.", psz_node
);
2693 xml_ReaderDelete( p_xml
, p_xml_reader
);
2694 p_xml_reader
= NULL
;
2706 uint32_t i_runs
= 0;
2707 uint32_t i_k_runs
= 0;
2708 uint32_t *pi_run_lengths
= NULL
;
2709 uint32_t *pi_k_run_lengths
= NULL
;
2710 uint32_t *pi_k_durations
= NULL
;
2711 ft_style_t
**pp_styles
= NULL
;
2713 line_desc_t
*p_lines
= NULL
;
2715 psz_text
= (uint32_t *)malloc( strlen( p_region_in
->psz_html
) *
2716 sizeof( uint32_t ) );
2721 rv
= ProcessNodes( p_filter
, p_xml_reader
,
2722 p_region_in
->p_style
, psz_text
, &i_len
,
2723 &i_runs
, &pi_run_lengths
, &pp_styles
,
2724 b_karaoke
, &i_k_runs
, &pi_k_run_lengths
,
2727 p_region_out
->i_x
= p_region_in
->i_x
;
2728 p_region_out
->i_y
= p_region_in
->i_y
;
2730 if(( rv
== VLC_SUCCESS
) && ( i_len
> 0 ))
2732 rv
= ProcessLines( p_filter
, psz_text
, i_len
, i_runs
,
2733 pi_run_lengths
, pp_styles
, &p_lines
, &result
,
2734 b_karaoke
, i_k_runs
, pi_k_run_lengths
,
2738 for( k
=0; k
<i_runs
; k
++)
2739 DeleteStyle( pp_styles
[k
] );
2741 free( pi_run_lengths
);
2744 /* Don't attempt to render text that couldn't be layed out
2747 if(( rv
== VLC_SUCCESS
) && ( i_len
> 0 ))
2749 if( config_GetInt( p_filter
, "freetype-yuvp" ) )
2751 Render( p_filter
, p_region_out
, p_lines
,
2752 result
.x
, result
.y
);
2756 RenderYUVA( p_filter
, p_region_out
, p_lines
,
2757 result
.x
, result
.y
);
2761 FreeLines( p_lines
);
2763 xml_ReaderDelete( p_xml
, p_xml_reader
);
2765 xml_Delete( p_xml
);
2767 stream_Delete( p_sub
);
2773 static char* FontConfig_Select( FcConfig
* priv
, const char* family
,
2774 bool b_bold
, bool b_italic
, int *i_idx
)
2777 FcPattern
*pat
, *p_pat
;
2781 pat
= FcPatternCreate();
2782 if (!pat
) return NULL
;
2784 FcPatternAddString( pat
, FC_FAMILY
, (const FcChar8
*)family
);
2785 FcPatternAddBool( pat
, FC_OUTLINE
, FcTrue
);
2786 FcPatternAddInteger( pat
, FC_SLANT
, b_italic
? 1000 : 0 );
2787 FcPatternAddInteger( pat
, FC_WEIGHT
, b_bold
? 1000 : 0 );
2789 FcDefaultSubstitute( pat
);
2791 if( !FcConfigSubstitute( priv
, pat
, FcMatchPattern
) )
2793 FcPatternDestroy( pat
);
2797 p_pat
= FcFontMatch( priv
, pat
, &result
);
2798 FcPatternDestroy( pat
);
2799 if( !p_pat
) return NULL
;
2801 if( ( FcResultMatch
!= FcPatternGetBool( p_pat
, FC_OUTLINE
, 0, &val_b
) )
2802 || ( val_b
!= FcTrue
) )
2804 FcPatternDestroy( p_pat
);
2807 if( FcResultMatch
!= FcPatternGetInteger( p_pat
, FC_INDEX
, 0, i_idx
) )
2812 if( FcResultMatch
!= FcPatternGetString( p_pat
, FC_FAMILY
, 0, &val_s
) )
2814 FcPatternDestroy( p_pat
);
2819 if( strcasecmp((const char*)val_s, family ) != 0 )
2820 msg_Warn( p_filter, "fontconfig: selected font family is not"
2821 "the requested one: '%s' != '%s'\n",
2822 (const char*)val_s, family );
2825 if( FcResultMatch
!= FcPatternGetString( p_pat
, FC_FILE
, 0, &val_s
) )
2827 FcPatternDestroy( p_pat
);
2831 FcPatternDestroy( p_pat
);
2832 return strdup( (const char*)val_s
);
2836 static void FreeLine( line_desc_t
*p_line
)
2839 for( i
= 0; p_line
->pp_glyphs
[ i
] != NULL
; i
++ )
2841 FT_Done_Glyph( (FT_Glyph
)p_line
->pp_glyphs
[ i
] );
2843 free( p_line
->pp_glyphs
);
2844 free( p_line
->p_glyph_pos
);
2845 free( p_line
->p_fg_rgb
);
2846 free( p_line
->p_bg_rgb
);
2847 free( p_line
->p_fg_bg_ratio
);
2848 free( p_line
->pi_underline_offset
);
2849 free( p_line
->pi_underline_thickness
);
2853 static void FreeLines( line_desc_t
*p_lines
)
2855 line_desc_t
*p_line
, *p_next
;
2857 if( !p_lines
) return;
2859 for( p_line
= p_lines
; p_line
!= NULL
; p_line
= p_next
)
2861 p_next
= p_line
->p_next
;
2866 static line_desc_t
*NewLine( int i_count
)
2868 line_desc_t
*p_line
= malloc( sizeof(line_desc_t
) );
2870 if( !p_line
) return NULL
;
2871 p_line
->i_height
= 0;
2872 p_line
->i_width
= 0;
2873 p_line
->p_next
= NULL
;
2875 p_line
->pp_glyphs
= malloc( sizeof(FT_BitmapGlyph
) * ( i_count
+ 1 ) );
2876 p_line
->p_glyph_pos
= malloc( sizeof( FT_Vector
) * ( i_count
+ 1 ) );
2877 p_line
->p_fg_rgb
= malloc( sizeof( uint32_t ) * ( i_count
+ 1 ) );
2878 p_line
->p_bg_rgb
= malloc( sizeof( uint32_t ) * ( i_count
+ 1 ) );
2879 p_line
->p_fg_bg_ratio
= calloc( i_count
+ 1, sizeof( uint8_t ) );
2880 p_line
->pi_underline_offset
= calloc( i_count
+ 1, sizeof( uint16_t ) );
2881 p_line
->pi_underline_thickness
= calloc( i_count
+ 1, sizeof( uint16_t ) );
2882 if( ( p_line
->pp_glyphs
== NULL
) ||
2883 ( p_line
->p_glyph_pos
== NULL
) ||
2884 ( p_line
->p_fg_rgb
== NULL
) ||
2885 ( p_line
->p_bg_rgb
== NULL
) ||
2886 ( p_line
->p_fg_bg_ratio
== NULL
) ||
2887 ( p_line
->pi_underline_offset
== NULL
) ||
2888 ( p_line
->pi_underline_thickness
== NULL
) )
2890 free( p_line
->pi_underline_thickness
);
2891 free( p_line
->pi_underline_offset
);
2892 free( p_line
->p_fg_rgb
);
2893 free( p_line
->p_bg_rgb
);
2894 free( p_line
->p_fg_bg_ratio
);
2895 free( p_line
->p_glyph_pos
);
2896 free( p_line
->pp_glyphs
);
2900 p_line
->pp_glyphs
[0] = NULL
;
2901 p_line
->b_new_color_mode
= false;
2906 static int GetFontSize( filter_t
*p_filter
)
2908 filter_sys_t
*p_sys
= p_filter
->p_sys
;
2912 if( p_sys
->i_default_font_size
)
2914 if( VLC_SUCCESS
== var_Get( p_filter
, "scale", &val
))
2915 i_size
= p_sys
->i_default_font_size
* val
.i_int
/ 1000;
2917 i_size
= p_sys
->i_default_font_size
;
2921 var_Get( p_filter
, "freetype-rel-fontsize", &val
);
2922 i_size
= (int)p_filter
->fmt_out
.video
.i_height
/ val
.i_int
;
2923 p_filter
->p_sys
->i_display_height
=
2924 p_filter
->fmt_out
.video
.i_height
;
2928 msg_Warn( p_filter
, "invalid fontsize, using 12" );
2929 if( VLC_SUCCESS
== var_Get( p_filter
, "scale", &val
))
2930 i_size
= 12 * val
.i_int
/ 1000;
2937 static int SetFontSize( filter_t
*p_filter
, int i_size
)
2939 filter_sys_t
*p_sys
= p_filter
->p_sys
;
2943 i_size
= GetFontSize( p_filter
);
2945 msg_Dbg( p_filter
, "using fontsize: %i", i_size
);
2948 p_sys
->i_font_size
= i_size
;
2950 if( FT_Set_Pixel_Sizes( p_sys
->p_face
, 0, i_size
) )
2952 msg_Err( p_filter
, "couldn't set font size to %d", i_size
);
2953 return VLC_EGENERIC
;
2959 static void YUVFromRGB( uint32_t i_argb
,
2960 uint8_t *pi_y
, uint8_t *pi_u
, uint8_t *pi_v
)
2962 int i_red
= ( i_argb
& 0x00ff0000 ) >> 16;
2963 int i_green
= ( i_argb
& 0x0000ff00 ) >> 8;
2964 int i_blue
= ( i_argb
& 0x000000ff );
2966 *pi_y
= (uint8_t)__MIN(abs( 2104 * i_red
+ 4130 * i_green
+
2967 802 * i_blue
+ 4096 + 131072 ) >> 13, 235);
2968 *pi_u
= (uint8_t)__MIN(abs( -1214 * i_red
+ -2384 * i_green
+
2969 3598 * i_blue
+ 4096 + 1048576) >> 13, 240);
2970 *pi_v
= (uint8_t)__MIN(abs( 3598 * i_red
+ -3013 * i_green
+
2971 -585 * i_blue
+ 4096 + 1048576) >> 13, 240);