Don't use deprecated and removed API in Mozilla plugin.
[vlc.git] / modules / misc / freetype.c
blob1f5850c4d67599b85e74b7b015d5913196fc47ce
1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
5 * $Id$
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 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_vout.h>
37 #include <vlc_osd.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
41 #include <vlc_xml.h>
42 #include <vlc_input.h>
44 #include <math.h>
45 #include <errno.h>
47 #include <ft2build.h>
48 #include FT_FREETYPE_H
49 #include FT_GLYPH_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)
54 #ifdef __APPLE__
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"
63 #else
64 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
65 #define FC_DEFAULT_FONT "Serif Bold"
66 #endif
68 #if defined(HAVE_FRIBIDI)
69 #include <fribidi/fribidi.h>
70 #endif
72 #ifdef HAVE_FONTCONFIG
73 #include <fontconfig/fontconfig.h>
74 #endif
76 #include <assert.h>
78 typedef struct line_desc_t line_desc_t;
80 /*****************************************************************************
81 * Local prototypes
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 *,
95 bool, bool, int * );
96 static int BuildDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
97 void* );
98 #endif
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 /*****************************************************************************
107 * Module descriptor
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") };
157 vlc_module_begin();
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,
164 false );
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 );
190 vlc_module_end();
192 struct 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. */
202 uint32_t *p_fg_rgb;
203 uint32_t *p_bg_rgb;
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;
210 int i_height;
211 int i_width;
212 int i_red, i_green, i_blue;
213 int i_alpha;
215 line_desc_t *p_next;
218 typedef struct font_stack_t font_stack_t;
219 struct font_stack_t
221 char *psz_name;
222 int i_size;
223 uint32_t i_color; /* ARGB */
224 uint32_t i_karaoke_bg_color; /* ARGB */
226 font_stack_t *p_next;
229 typedef struct
231 int i_font_size;
232 uint32_t i_font_color; /* ARGB */
233 uint32_t i_karaoke_bg_color; /* ARGB */
234 bool b_italic;
235 bool b_bold;
236 bool b_underline;
237 char *psz_fontname;
238 } ft_style_t;
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);
245 #endif
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 *****************************************************************************/
253 struct filter_sys_t
255 FT_Library p_library; /* handle to library */
256 FT_Face p_face; /* handle to face object */
257 bool i_use_kerning;
258 uint8_t i_font_opacity;
259 int i_font_color;
260 int i_font_size;
261 int i_effect;
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;
269 #endif
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;
283 filter_sys_t *p_sys;
284 char *psz_fontfile = NULL;
285 int i_error;
286 vlc_value_t val;
287 vlc_object_t *p_fontbuilder;
289 /* Allocate structure */
290 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
291 if( !p_sys )
293 msg_Err( p_filter, "out of memory" );
294 return VLC_ENOMEM;
296 p_sys->p_face = 0;
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 );
326 if( !psz_fontfile )
328 msg_Err( p_filter, "out of memory" );
329 goto error;
331 #ifdef WIN32
332 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
333 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
334 #elif defined(__APPLE__)
335 strcpy( psz_fontfile, DEFAULT_FONT );
336 #else
337 msg_Err( p_filter, "user didn't specify a font" );
338 goto error;
339 #endif
342 i_error = FT_Init_FreeType( &p_sys->p_library );
343 if( i_error )
345 msg_Err( p_filter, "couldn't initialize freetype" );
346 goto error;
348 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
349 0, &p_sys->p_face );
350 if( i_error == FT_Err_Unknown_File_Format )
352 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
353 goto error;
355 else if( i_error )
357 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
358 goto error;
361 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
362 if( i_error )
364 msg_Err( p_filter, "font has no unicode translation table" );
365 goto error;
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,
376 "fontlist builder",
377 FIND_CHILD );
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 );
390 if( p_fontbuilder )
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,
400 "fontlist builder",
401 FontBuilder,
402 VLC_THREAD_PRIORITY_LOW,
403 false ) )
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." );
413 else
415 vlc_object_release( p_fontbuilder );
418 else
420 vlc_object_release( p_fontbuilder );
422 vlc_mutex_unlock( lock );
424 #endif
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;
440 #else
441 p_filter->pf_render_html = NULL;
442 #endif
444 LoadFontsFromAttachments( p_filter );
446 return VLC_SUCCESS;
448 error:
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 );
452 free( p_sys );
453 return VLC_EGENERIC;
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 )
468 int k;
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 );
482 if( p_fontbuilder )
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. */
499 #endif
500 FT_Done_Face( p_sys->p_face );
501 FT_Done_FreeType( p_sys->p_library );
502 free( p_sys );
505 #ifdef HAVE_FONTCONFIG
507 static void FontBuilder( vlc_object_t *p_this )
509 FcConfig *p_fontconfig = FcInitLoadConfig();
510 vlc_mutex_t *lock;
512 vlc_thread_ready( p_this );
514 if( p_fontconfig )
516 mtime_t t1, t2;
518 msg_Dbg( p_this, "Building font database..." );
519 t1 = mdate();
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" );
529 t2 = mdate();
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 );
545 #endif
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;
557 int k;
558 int rv = VLC_SUCCESS;
560 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
561 if( ! p_input )
562 return VLC_EGENERIC;
564 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
566 vlc_object_release(p_input);
567 return VLC_EGENERIC;
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 )
573 rv = VLC_ENOMEM;
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;
588 else
590 vlc_input_attachment_Delete( p_attach );
593 else
595 vlc_input_attachment_Delete( p_attach );
598 free( pp_attachments );
600 vlc_object_release(p_input);
602 return rv;
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};
617 uint8_t *p_dst;
618 video_format_t fmt;
619 int i, x, y, i_pitch;
620 uint8_t i_y; /* YUV values, derived from incoming RGB */
621 int8_t i_u, i_v;
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');
627 fmt.i_aspect = 0;
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 );
636 if( !p_region_tmp )
638 msg_Err( p_filter, "cannot allocate SPU region" );
639 return VLC_EGENERIC;
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;
654 /* Build palette */
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 +
710 i_align_offset;
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] )
717 p_dst[i_offset+x] =
718 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
720 i_offset += i_pitch;
725 /* Outlining (find something better than nearest neighbour filtering ?) */
726 if( 1 )
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;
736 left = 0;
738 for( x = 1; x < (int)fmt.i_width - 1; x++ )
740 current = p_dst[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;
743 left = current;
746 memset( p_top, 0, fmt.i_width );
749 return VLC_SUCCESS;
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)
759 int y, x, z;
760 int i_pitch;
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;
776 if( b_ul_next_char )
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++ )
783 bool b_ok = true;
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;
788 z++ )
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] )
797 b_ok = false;
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] )
807 b_ok = false;
812 if( b_ok )
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;
820 i_offset += i_pitch;
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;
828 int x,y;
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]) )
868 p_dst[i_offset+x] =
869 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
871 i_offset += i_pitch;
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;
887 video_format_t fmt;
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 )
893 return VLC_SUCCESS;
895 /* Create a new subpicture region */
896 memset( &fmt, 0, sizeof(video_format_t) );
897 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
898 fmt.i_aspect = 0;
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 );
907 if( !p_region_tmp )
909 msg_Err( p_filter, "cannot allocate SPU region" );
910 return VLC_EGENERIC;
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) |
920 (p_line->i_blue ),
921 &i_y, &i_u, &i_v);
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 );
938 else
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 +
1007 i_align_offset;
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;
1028 if( x > i_split )
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,
1058 i_y, i_u, i_v,
1059 p_region);
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;
1068 return VLC_SUCCESS;
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;
1085 char *psz_string;
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;
1088 vlc_value_t val;
1089 int i_scale = 1000;
1091 FT_BBox line;
1092 FT_BBox glyph_size;
1093 FT_Vector result;
1094 FT_Glyph tmp_glyph;
1096 /* Sanity check */
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;
1110 else
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" );
1133 goto error;
1135 #if defined(WORDS_BIGENDIAN)
1136 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1137 #else
1138 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1139 #endif
1140 if( iconv_handle == (vlc_iconv_t)-1 )
1142 msg_Warn( p_filter, "unable to do conversion" );
1143 goto error;
1147 char *p_out_buffer;
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,
1155 &i_in_bytes,
1156 &p_out_buffer, &i_out_bytes_left );
1158 vlc_iconv_close( iconv_handle );
1160 if( i_in_bytes )
1162 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1163 "bytes left %u", (unsigned)i_in_bytes );
1164 goto error;
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" );
1179 goto error;
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')
1189 break;
1190 p_fribidi_string[pos] = i_char;
1191 ++pos;
1193 start_pos = pos;
1194 while( pos < i_string_length )
1196 i_char = psz_unicode[pos];
1197 if (i_char == '\r' || i_char == '\n')
1198 break;
1199 ++pos;
1201 if (pos > start_pos)
1203 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1204 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1205 pos - start_pos,
1206 &base_dir,
1207 (FriBidiChar*)p_fribidi_string + start_pos,
1208 0, 0, 0);
1212 free( psz_unicode_orig );
1213 psz_unicode = psz_unicode_orig = p_fribidi_string;
1214 p_fribidi_string[ i_string_length ] = 0;
1216 #endif
1218 /* Calculate relative glyph positions and a bounding box for the
1219 * entire string */
1220 if( !(p_line = NewLine( strlen( psz_string ))) )
1222 msg_Err( p_filter, "out of memory" );
1223 goto error;
1225 p_lines = p_line;
1226 i_pen_x = i_pen_y = 0;
1227 i_previous = i = 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 */
1238 continue;
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" );
1247 goto error;
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;
1257 p_prev = p_line;
1258 p_line = p_next;
1259 result.x = __MAX( result.x, line.xMax );
1260 result.y += face->size->metrics.height >> 6;
1261 i_pen_x = 0;
1262 i_previous = i = 0;
1263 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1264 i_pen_y += face->size->metrics.height >> 6;
1265 #if 0
1266 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1267 #endif
1268 continue;
1271 i_glyph_index = FT_Get_Char_Index( face, i_char );
1272 if( p_sys->i_use_kerning && i_glyph_index
1273 && i_previous )
1275 FT_Vector delta;
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 );
1284 if( i_error )
1286 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1287 " %d", i_error );
1288 goto error;
1290 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1291 if( i_error )
1293 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1294 "%d", i_error );
1295 goto error;
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);
1299 if( i_error )
1301 FT_Done_Glyph( tmp_glyph );
1302 continue;
1304 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1306 /* Do rest */
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;
1313 FreeLine( p_line );
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 != ' ' )
1321 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 != '.' )
1330 psz_unicode--;
1333 if( psz_unicode == psz_line_start )
1335 msg_Warn( p_filter, "unbreakable string" );
1336 goto error;
1338 else
1340 *psz_unicode = '\n';
1342 psz_unicode = psz_line_start;
1343 i_pen_x = 0;
1344 i_previous = i = 0;
1345 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1346 continue;
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;
1353 i++;
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;
1366 #undef face
1367 #undef glyph
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 );
1374 else
1375 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1377 free( psz_unicode_orig );
1378 FreeLines( p_lines );
1379 return VLC_SUCCESS;
1381 error:
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 ));
1394 if( p_style )
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 );
1405 return p_style;
1408 static void DeleteStyle( ft_style_t *p_style )
1410 if( p_style )
1412 free( p_style->psz_fontname );
1413 free( p_style );
1417 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1419 if( !s1 || !s2 )
1420 return false;
1421 if( s1 == s2 )
1422 return true;
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 )))
1431 return true;
1433 return false;
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;
1441 if( !p_font )
1442 return VLC_EGENERIC;
1444 p_new = malloc( sizeof( font_stack_t ) );
1445 if( ! p_new )
1446 return VLC_ENOMEM;
1448 p_new->p_next = NULL;
1450 if( psz_name )
1451 p_new->psz_name = strdup( psz_name );
1452 else
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;
1459 if( !*p_font )
1461 *p_font = p_new;
1463 else
1465 font_stack_t *p_last;
1467 for( p_last = *p_font;
1468 p_last->p_next;
1469 p_last = p_last->p_next )
1472 p_last->p_next = p_new;
1474 return VLC_SUCCESS;
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;
1486 p_last->p_next;
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;
1494 else
1495 *p_font = NULL;
1497 free( p_last->psz_name );
1498 free( p_last );
1500 return VLC_SUCCESS;
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;
1512 p_last->p_next;
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;
1521 return VLC_SUCCESS;
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 */
1539 if( *ppsz_unicode )
1541 #if defined(WORDS_BIGENDIAN)
1542 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1543 #else
1544 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1545 #endif
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 );
1560 if( i_in_bytes )
1562 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1563 "bytes left %u", (unsigned)i_in_bytes );
1565 else
1567 *(uint32_t*)p_out_buffer = 0;
1568 *i_string_length =
1569 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1572 else
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,
1581 bool b_uline )
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 );
1596 return p_style;
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 )
1605 FT_BBox line;
1606 int i_yMin, i_yMax;
1607 int i;
1608 bool b_first_on_line = true;
1610 int i_previous = 0;
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++ )
1620 FT_BBox glyph_size;
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 );
1630 i_yMin = line.yMin;
1631 i_yMax = line.yMax;
1633 if( line.xMax > 0 )
1634 b_first_on_line = false;
1636 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1638 FT_BBox glyph_size;
1639 FT_Glyph tmp_glyph;
1640 int i_error;
1642 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1643 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1644 && i_previous )
1646 FT_Vector delta;
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 );
1655 if( i_error )
1657 msg_Err( p_filter,
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 );
1663 if( i_error )
1665 msg_Err( p_filter,
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);
1672 if( i_error )
1674 FT_Done_Glyph( tmp_glyph );
1675 continue;
1677 if( b_uline )
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 != ' ' )
1705 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 ) );
1726 *pi_start = i;
1727 return VLC_SUCCESS;
1729 else
1731 *psz_unicode = '\n';
1733 psz_unicode = psz_unicode_start;
1734 *pi_pen_x = i_pen_x_start;
1735 i_previous = 0;
1737 line.yMax = i_yMax;
1738 line.yMin = i_yMin;
1740 continue;
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;
1747 i++;
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 ) );
1758 *pi_start = i;
1760 /* Get rid of any text processed - if necessary repositioning
1761 * at the start of a new line of text
1763 if( !*psz_unicode )
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';
1774 return VLC_SUCCESS;
1777 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1778 font_stack_t **p_fonts, int i_scale )
1780 int rv;
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,
1791 &psz_fontname,
1792 &i_font_size,
1793 &i_font_color,
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;
1827 else
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;
1842 free( psz_name );
1843 free( psz_value );
1846 rv = PushFont( p_fonts,
1847 psz_fontname,
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 );
1854 return rv;
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 )
1869 (*pi_runs)++;
1871 if( *ppp_styles )
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 */
1884 if( *ppp_styles )
1886 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1887 p_style = NULL;
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 )
1922 int i_chars = 0;
1923 uint32_t i;
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
1931 * karaoke tags
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 )
1954 (*pi_k_runs)++;
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;
1986 free( psz_name );
1987 free( psz_value );
1991 static int ProcessNodes( filter_t *p_filter,
1992 xml_reader_t *p_xml_reader,
1993 text_style_t *p_font_style,
1994 uint32_t *psz_text,
1995 int *pi_len,
1997 uint32_t *pi_runs,
1998 uint32_t **ppi_run_lengths,
1999 ft_style_t ***ppp_styles,
2001 bool b_karaoke,
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;
2010 vlc_value_t val;
2011 int i_scale = 1000;
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;
2022 if( p_font_style )
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 )
2033 b_bold = true;
2034 if( p_font_style->i_style_flags & STYLE_ITALIC )
2035 b_italic = true;
2036 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2037 b_uline = true;
2039 else
2041 rv = PushFont( &p_fonts,
2042 FC_DEFAULT_FONT,
2043 p_sys->i_font_size,
2044 0x00ffffff,
2045 0x00ffffff );
2047 if( rv != VLC_SUCCESS )
2048 return rv;
2050 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2052 switch ( xml_ReaderNodeType( p_xml_reader ) )
2054 case XML_READER_NONE:
2055 break;
2056 case XML_READER_ENDELEM:
2057 psz_node = xml_ReaderName( p_xml_reader );
2059 if( psz_node )
2061 if( !strcasecmp( "font", psz_node ) )
2062 PopFont( &p_fonts );
2063 else if( !strcasecmp( "b", psz_node ) )
2064 b_bold = false;
2065 else if( !strcasecmp( "i", psz_node ) )
2066 b_italic = false;
2067 else if( !strcasecmp( "u", psz_node ) )
2068 b_uline = false;
2070 free( psz_node );
2072 break;
2073 case XML_READER_STARTELEM:
2074 psz_node = xml_ReaderName( p_xml_reader );
2075 if( psz_node )
2077 if( !strcasecmp( "font", psz_node ) )
2078 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2079 else if( !strcasecmp( "b", psz_node ) )
2080 b_bold = true;
2081 else if( !strcasecmp( "i", psz_node ) )
2082 b_italic = true;
2083 else if( !strcasecmp( "u", psz_node ) )
2084 b_uline = true;
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,
2090 &p_fonts,
2091 b_bold,
2092 b_italic,
2093 b_uline ) );
2095 else if( !strcasecmp( "k", psz_node ) )
2097 /* Only valid in karaoke */
2098 if( b_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 );
2110 free( psz_node );
2112 break;
2113 case XML_READER_TEXT:
2114 psz_node = xml_ReaderValue( p_xml_reader );
2115 if( psz_node )
2117 /* Turn any multiple-whitespaces into single spaces */
2118 char *s = strpbrk( psz_node, "\t\r\n " );
2119 while( s )
2121 int i_whitespace = strspn( s, "\t\r\n " );
2123 if( i_whitespace > 1 )
2124 memmove( &s[1],
2125 &s[i_whitespace],
2126 strlen( s ) - i_whitespace + 1 );
2127 *s++ = ' ';
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,
2134 &p_fonts,
2135 b_bold,
2136 b_italic,
2137 b_uline ) );
2138 free( psz_node );
2140 break;
2142 if( rv != VLC_SUCCESS )
2144 psz_text = psz_text_orig;
2145 break;
2148 if( b_karaoke )
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 ) );
2158 return rv;
2161 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2163 int k;
2165 for( k=0; k < p_sys->i_font_attachments; k++ )
2167 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2168 int i_font_idx = 0;
2169 FT_Face p_face = NULL;
2171 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2172 p_attach->p_data,
2173 p_attach->i_data,
2174 i_font_idx,
2175 &p_face ))
2177 if( p_face )
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;
2184 else
2185 match = match && !p_style->b_bold;
2187 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2188 match = match && p_style->b_italic;
2189 else
2190 match = match && !p_style->b_italic;
2192 if( match )
2194 *pp_face = p_face;
2195 return VLC_SUCCESS;
2198 FT_Done_Face( p_face );
2200 i_font_idx++;
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 )
2209 (void)p_this;
2210 (void)psz_var;
2211 (void)oldval;
2212 ((filter_sys_t*)param)->b_fontconfig_ok = newval.b_bool;
2213 assert( newval.b_bool );
2214 return VLC_SUCCESS;
2217 static int ProcessLines( filter_t *p_filter,
2218 uint32_t *psz_text,
2219 int i_len,
2221 uint32_t i_runs,
2222 uint32_t *pi_run_lengths,
2223 ft_style_t **pp_styles,
2224 line_desc_t **pp_lines,
2226 FT_Vector *p_result,
2228 bool b_karaoke,
2229 uint32_t i_k_runs,
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;
2238 uint32_t i, j, k;
2239 int i_prev;
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 )
2247 return VLC_ENOMEM;
2249 if( b_karaoke )
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
2254 * get the text.
2258 i = 0;
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 ||
2286 ! p_levels )
2288 msg_Err( p_filter, "out of memory" );
2289 free( p_levels );
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 );
2297 return VLC_ENOMEM;
2300 /* Do bidi conversion line-by-line */
2301 while(pos < i_len)
2303 while(pos < i_len) {
2304 if (psz_text[pos] != '\n')
2305 break;
2306 p_fribidi_string[pos] = psz_text[pos];
2307 pp_char_styles_new[pos] = pp_char_styles[pos];
2308 p_new_positions[pos] = pos;
2309 p_levels[pos] = 0;
2310 ++pos;
2312 start_pos = pos;
2313 while(pos < i_len) {
2314 if (psz_text[pos] == '\n')
2315 break;
2316 ++pos;
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,
2325 p_old_positions,
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;
2341 #endif
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 */
2361 fraction = 1.0;
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 */
2368 fraction = 0.0;
2370 else
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 ];
2383 else
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;
2390 else
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 ];
2402 free( p_levels );
2403 free( p_new_positions );
2405 FT_Vector tmp_result;
2407 line_desc_t *p_line = NULL;
2408 line_desc_t *p_prev = NULL;
2410 int i_pen_x = 0;
2411 int i_pen_y = 0;
2412 int i_posn = 0;
2414 p_result->x = p_result->y = 0;
2415 tmp_result.x = tmp_result.y = 0;
2417 i_prev = 0;
2418 for( k = 0; k <= (uint32_t) i_len; k++ )
2420 if( ( k == (uint32_t) i_len ) ||
2421 ( ( k > 0 ) &&
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;
2428 int i_idx = 0;
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 )
2435 char *psz_fontfile;
2436 vlc_mutex_lock( &p_sys->fontconfig_lock );
2438 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2439 p_style->psz_fontname,
2440 p_style->b_bold,
2441 p_style->b_italic,
2442 &i_idx );
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;
2456 if( psz_fontfile )
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)
2464 free( psz_text );
2465 #endif
2466 free( pi_karaoke_bar );
2467 return VLC_EGENERIC;
2469 free( psz_fontfile );
2472 if( p_face &&
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 );
2479 p_face = NULL;
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)
2490 free( psz_text );
2491 #endif
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 ));
2501 if( !psz_unicode )
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)
2508 free( psz_text );
2509 #endif
2510 free( pi_karaoke_bar );
2511 return VLC_ENOMEM;
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 )
2518 if( !p_line )
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)
2527 free( psz_text );
2528 #endif
2529 free( pi_karaoke_bar );
2530 return VLC_ENOMEM;
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;
2542 i_pen_x = 0;
2543 i_pen_y += tmp_result.y;
2544 tmp_result.x = 0;
2545 tmp_result.y = 0;
2546 i_posn = 0;
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)
2561 free( psz_text );
2562 #endif
2563 free( pi_karaoke_bar );
2564 return VLC_EGENERIC;
2567 if( *psz_unicode )
2569 p_result->x = __MAX( p_result->x, tmp_result.x );
2570 p_result->y += tmp_result.y;
2572 p_prev = p_line;
2573 p_line = NULL;
2575 if( *psz_unicode == '\n')
2577 uint32_t *c_ptr;
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 );
2588 i_prev = k;
2591 free( pp_char_styles );
2592 #if defined(HAVE_FRIBIDI)
2593 free( psz_text );
2594 #endif
2595 if( p_line )
2597 p_result->x = __MAX( p_result->x, tmp_result.x );
2598 p_result->y += tmp_result.y;
2601 if( pi_karaoke_bar )
2603 int i = 0;
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)
2610 /* do nothing */
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 ];
2620 else
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 */
2636 i++;
2638 free( pi_karaoke_bar );
2641 return VLC_SUCCESS;
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 ),
2661 true );
2662 if( p_sub )
2664 p_xml = xml_Create( p_filter );
2665 if( p_xml )
2667 bool b_karaoke = false;
2669 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2670 if( p_xml_reader )
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 );
2683 b_karaoke = true;
2685 else if( !strcasecmp( "text", psz_node ) )
2687 b_karaoke = false;
2689 else
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;
2695 rv = VLC_EGENERIC;
2698 free( psz_node );
2702 if( p_xml_reader )
2704 uint32_t *psz_text;
2705 int i_len = 0;
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;
2712 FT_Vector result;
2713 line_desc_t *p_lines = NULL;
2715 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2716 sizeof( uint32_t ) );
2717 if( psz_text )
2719 uint32_t k;
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,
2725 &pi_k_durations );
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,
2735 pi_k_durations );
2738 for( k=0; k<i_runs; k++)
2739 DeleteStyle( pp_styles[k] );
2740 free( pp_styles );
2741 free( pi_run_lengths );
2742 free( psz_text );
2744 /* Don't attempt to render text that couldn't be layed out
2745 * properly.
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 );
2754 else
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 );
2770 return rv;
2773 static char* FontConfig_Select( FcConfig* priv, const char* family,
2774 bool b_bold, bool b_italic, int *i_idx )
2776 FcResult result;
2777 FcPattern *pat, *p_pat;
2778 FcChar8* val_s;
2779 FcBool val_b;
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 );
2794 return NULL;
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 );
2805 return NULL;
2807 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2809 *i_idx = 0;
2812 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2814 FcPatternDestroy( p_pat );
2815 return NULL;
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 );
2828 return NULL;
2831 FcPatternDestroy( p_pat );
2832 return strdup( (const char*)val_s );
2834 #endif
2836 static void FreeLine( line_desc_t *p_line )
2838 unsigned int i;
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 );
2850 free( p_line );
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;
2862 FreeLine( p_line );
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 );
2897 free( p_line );
2898 return NULL;
2900 p_line->pp_glyphs[0] = NULL;
2901 p_line->b_new_color_mode = false;
2903 return p_line;
2906 static int GetFontSize( filter_t *p_filter )
2908 filter_sys_t *p_sys = p_filter->p_sys;
2909 vlc_value_t val;
2910 int i_size = 0;
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;
2916 else
2917 i_size = p_sys->i_default_font_size;
2919 else
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;
2926 if( i_size <= 0 )
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;
2931 else
2932 i_size = 12;
2934 return i_size;
2937 static int SetFontSize( filter_t *p_filter, int i_size )
2939 filter_sys_t *p_sys = p_filter->p_sys;
2941 if( !i_size )
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;
2956 return VLC_SUCCESS;
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);