Freetype: fallback to arial.ttf on Win32
[vlc.git] / modules / text_renderer / freetype.c
blob8ab61969055dcb836429439be5814c6215579e1f
1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2011 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>
10 * Jean-Baptiste Kempf <jb@videolan.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
28 * Preamble
29 *****************************************************************************/
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34 #include <math.h>
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38 #include <vlc_stream.h> /* stream_MemoryNew */
39 #include <vlc_input.h> /* vlc_input_attachment_* */
40 #include <vlc_xml.h> /* xml_reader */
41 #include <vlc_strings.h> /* resolve_xml_special_chars */
42 #include <vlc_charset.h> /* ToCharset */
43 #include <vlc_dialog.h> /* FcCache dialog */
44 #include <vlc_filter.h> /* filter_sys_t */
45 #include <vlc_text_style.h> /* text_style_t*/
47 /* Default fonts */
48 #ifdef __APPLE__
49 # define DEFAULT_FONT_FILE "/Library/Fonts/Arial Black.ttf"
50 # define DEFAULT_FAMILY "Arial Black"
51 #elif defined( WIN32 )
52 # define DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
53 # define DEFAULT_FAMILY "Arial"
54 #elif defined( HAVE_MAEMO )
55 # define DEFAULT_FONT_FILE "/usr/share/fonts/nokia/nosnb.ttf"
56 # define DEFAULT_FAMILY "Nokia Sans Bold"
57 #else
58 # define DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
59 # define DEFAULT_FAMILY "Serif Bold"
60 #endif
62 /* Freetype */
63 #include <freetype/ftsynth.h>
64 #include FT_FREETYPE_H
65 #include FT_GLYPH_H
66 #include FT_STROKER_H
68 #define FT_FLOOR(X) ((X & -64) >> 6)
69 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
70 #ifndef FT_MulFix
71 #define FT_MulFix(v, s) (((v)*(s))>>16)
72 #endif
74 /* RTL */
75 #if defined(HAVE_FRIBIDI)
76 # include <fribidi/fribidi.h>
77 #endif
79 /* Win32 GDI */
80 #ifdef WIN32
81 # include <windows.h>
82 # include <shlobj.h>
83 # define HAVE_STYLES
84 # undef HAVE_FONTCONFIG
85 #endif
87 /* FontConfig */
88 #ifdef HAVE_FONTCONFIG
89 # include <fontconfig/fontconfig.h>
90 # define HAVE_STYLES
91 #endif
93 #include <assert.h>
95 /*****************************************************************************
96 * Module descriptor
97 *****************************************************************************/
98 static int Create ( vlc_object_t * );
99 static void Destroy( vlc_object_t * );
101 #define FONT_TEXT N_("Font")
103 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
104 #define FONT_LONGTEXT N_("Font file for the font you want to use")
106 #define FONTSIZE_TEXT N_("Font size in pixels")
107 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
108 "that will be rendered on the video. " \
109 "If set to something different than 0 this option will override the " \
110 "relative font size." )
111 #define OPACITY_TEXT N_("Text opacity")
112 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
113 "text that will be rendered on the video. 0 = transparent, " \
114 "255 = totally opaque. " )
115 #define COLOR_TEXT N_("Text default color")
116 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
117 "the video. This must be an hexadecimal (like HTML colors). The first two "\
118 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
119 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
120 #define FONTSIZER_TEXT N_("Relative font size")
121 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
122 "fonts that will be rendered on the video. If absolute font size is set, "\
123 "relative size will be overridden." )
124 #define BOLD_TEXT N_("Force bold")
126 #define BG_OPACITY_TEXT N_("Background opacity")
127 #define BG_COLOR_TEXT N_("Background color")
129 #define OUTLINE_OPACITY_TEXT N_("Outline opacity")
130 #define OUTLINE_COLOR_TEXT N_("Outline color")
131 #define OUTLINE_THICKNESS_TEXT N_("Outline thickness")
133 #define SHADOW_OPACITY_TEXT N_("Shadow opacity")
134 #define SHADOW_COLOR_TEXT N_("Shadow color")
135 #define SHADOW_ANGLE_TEXT N_("Shadow angle")
136 #define SHADOW_DISTANCE_TEXT N_("Shadow distance")
139 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
140 static const char *const ppsz_sizes_text[] = {
141 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
142 #define YUVP_TEXT N_("Use YUVP renderer")
143 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
144 "This option is only needed if you want to encode into DVB subtitles" )
146 static const int pi_color_values[] = {
147 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
148 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
149 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
151 static const char *const ppsz_color_descriptions[] = {
152 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
153 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
154 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
156 static const int pi_outline_thickness[] = {
157 0, 2, 4, 6,
159 static const char *const ppsz_outline_thickness[] = {
160 N_("None"), N_("Thin"), N_("Normal"), N_("Thick"),
163 vlc_module_begin ()
164 set_shortname( N_("Text renderer"))
165 set_description( N_("Freetype2 font renderer") )
166 set_category( CAT_VIDEO )
167 set_subcategory( SUBCAT_VIDEO_SUBPIC )
169 #ifdef HAVE_STYLES
170 add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
171 #else
172 add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
173 #endif
175 add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
176 FONTSIZE_LONGTEXT, true )
177 change_safe()
179 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
180 FONTSIZER_LONGTEXT, false )
181 change_integer_list( pi_sizes, ppsz_sizes_text )
182 change_safe()
184 /* opacity valid on 0..255, with default 255 = fully opaque */
185 add_integer_with_range( "freetype-opacity", 255, 0, 255,
186 OPACITY_TEXT, OPACITY_LONGTEXT, false )
187 change_safe()
189 /* hook to the color values list, with default 0x00ffffff = white */
190 add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
191 COLOR_LONGTEXT, false )
192 change_integer_list( pi_color_values, ppsz_color_descriptions )
193 change_safe()
195 add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
196 change_safe()
198 add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
199 BG_OPACITY_TEXT, NULL, false )
200 change_safe()
201 add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
202 NULL, false )
203 change_integer_list( pi_color_values, ppsz_color_descriptions )
204 change_safe()
206 add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
207 OUTLINE_OPACITY_TEXT, NULL, false )
208 change_safe()
209 add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
210 NULL, false )
211 change_integer_list( pi_color_values, ppsz_color_descriptions )
212 change_safe()
213 add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
214 NULL, false )
215 change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
216 change_safe()
218 add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
219 SHADOW_OPACITY_TEXT, NULL, false )
220 change_safe()
221 add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
222 NULL, false )
223 change_integer_list( pi_color_values, ppsz_color_descriptions )
224 change_safe()
225 add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
226 SHADOW_ANGLE_TEXT, NULL, false )
227 change_safe()
228 add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
229 SHADOW_DISTANCE_TEXT, NULL, false )
230 change_safe()
232 add_obsolete_integer( "freetype-effect" );
234 add_bool( "freetype-yuvp", false, YUVP_TEXT,
235 YUVP_LONGTEXT, true )
236 set_capability( "text renderer", 100 )
237 add_shortcut( "text" )
238 set_callbacks( Create, Destroy )
239 vlc_module_end ()
242 /*****************************************************************************
243 * Local prototypes
244 *****************************************************************************/
246 typedef struct
248 FT_BitmapGlyph p_glyph;
249 FT_BitmapGlyph p_outline;
250 FT_BitmapGlyph p_shadow;
251 uint32_t i_color; /* ARGB color */
252 int i_line_offset; /* underline/strikethrough offset */
253 int i_line_thickness; /* underline/strikethrough thickness */
254 } line_character_t;
256 typedef struct line_desc_t line_desc_t;
257 struct line_desc_t
259 line_desc_t *p_next;
261 int i_width;
262 int i_base_line;
263 int i_character_count;
264 line_character_t *p_character;
267 typedef struct font_stack_t font_stack_t;
268 struct font_stack_t
270 char *psz_name;
271 int i_size;
272 uint32_t i_color; /* ARGB */
273 uint32_t i_karaoke_bg_color; /* ARGB */
275 font_stack_t *p_next;
278 /*****************************************************************************
279 * filter_sys_t: freetype local data
280 *****************************************************************************
281 * This structure is part of the video output thread descriptor.
282 * It describes the freetype specific properties of an output thread.
283 *****************************************************************************/
284 struct filter_sys_t
286 FT_Library p_library; /* handle to library */
287 FT_Face p_face; /* handle to face object */
288 FT_Stroker p_stroker;
289 uint8_t i_font_opacity;
290 int i_font_color;
291 int i_font_size;
292 bool b_font_bold;
294 uint8_t i_background_opacity;
295 int i_background_color;
297 double f_outline_thickness;
298 uint8_t i_outline_opacity;
299 int i_outline_color;
301 float f_shadow_vector_x;
302 float f_shadow_vector_y;
303 uint8_t i_shadow_opacity;
304 int i_shadow_color;
306 int i_default_font_size;
307 int i_display_height;
308 char* psz_fontfamily;
309 #ifdef HAVE_STYLES
310 xml_reader_t *p_xml;
311 #ifdef WIN32
312 char* psz_win_fonts_path;
313 #endif
314 #endif
316 input_attachment_t **pp_font_attachments;
317 int i_font_attachments;
320 /* */
321 static void YUVFromRGB( uint32_t i_argb,
322 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
324 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
325 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
326 int i_blue = ( i_argb & 0x000000ff );
328 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
329 802 * i_blue + 4096 + 131072 ) >> 13, 235);
330 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
331 3598 * i_blue + 4096 + 1048576) >> 13, 240);
332 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
333 -585 * i_blue + 4096 + 1048576) >> 13, 240);
335 static void RGBFromRGB( uint32_t i_argb,
336 uint8_t *pi_r, uint8_t *pi_g, uint8_t *pi_b )
338 *pi_r = ( i_argb & 0x00ff0000 ) >> 16;
339 *pi_g = ( i_argb & 0x0000ff00 ) >> 8;
340 *pi_b = ( i_argb & 0x000000ff );
342 /*****************************************************************************
343 * Make any TTF/OTF fonts present in the attachments of the media file
344 * and store them for later use by the FreeType Engine
345 *****************************************************************************/
346 static int LoadFontsFromAttachments( filter_t *p_filter )
348 filter_sys_t *p_sys = p_filter->p_sys;
349 input_attachment_t **pp_attachments;
350 int i_attachments_cnt;
352 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
353 return VLC_EGENERIC;
355 p_sys->i_font_attachments = 0;
356 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
357 if( !p_sys->pp_font_attachments )
358 return VLC_ENOMEM;
360 for( int k = 0; k < i_attachments_cnt; k++ )
362 input_attachment_t *p_attach = pp_attachments[k];
364 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
365 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
366 p_attach->i_data > 0 && p_attach->p_data )
368 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
370 else
372 vlc_input_attachment_Delete( p_attach );
375 free( pp_attachments );
377 return VLC_SUCCESS;
380 static int GetFontSize( filter_t *p_filter )
382 filter_sys_t *p_sys = p_filter->p_sys;
383 int i_size = 0;
385 if( p_sys->i_default_font_size )
387 i_size = p_sys->i_default_font_size;
389 else
391 int i_ratio = var_GetInteger( p_filter, "freetype-rel-fontsize" );
392 if( i_ratio > 0 )
394 i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
395 p_filter->p_sys->i_display_height = p_filter->fmt_out.video.i_height;
398 if( i_size <= 0 )
400 msg_Warn( p_filter, "invalid fontsize, using 12" );
401 i_size = 12;
403 return i_size;
406 static int SetFontSize( filter_t *p_filter, int i_size )
408 filter_sys_t *p_sys = p_filter->p_sys;
410 if( !i_size )
412 i_size = GetFontSize( p_filter );
414 msg_Dbg( p_filter, "using fontsize: %i", i_size );
417 p_sys->i_font_size = i_size;
419 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
421 msg_Err( p_filter, "couldn't set font size to %d", i_size );
422 return VLC_EGENERIC;
425 return VLC_SUCCESS;
428 #ifdef HAVE_STYLES
429 #ifdef HAVE_FONTCONFIG
430 static void FontConfig_BuildCache( filter_t *p_filter )
432 /* */
433 msg_Dbg( p_filter, "Building font databases.");
434 mtime_t t1, t2;
435 t1 = mdate();
437 #ifdef WIN32
438 dialog_progress_bar_t *p_dialog = NULL;
439 FcConfig *fcConfig = FcInitLoadConfig();
441 p_dialog = dialog_ProgressCreate( p_filter,
442 _("Building font cache"),
443 _("Please wait while your font cache is rebuilt.\n"
444 "This should take less than a few minutes."), NULL );
446 /* if( p_dialog )
447 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
449 FcConfigBuildFonts( fcConfig );
450 if( p_dialog )
452 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
453 dialog_ProgressDestroy( p_dialog );
454 p_dialog = NULL;
456 #endif
457 t2 = mdate();
458 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
461 /***
462 * \brief Selects a font matching family, bold, italic provided
463 ***/
464 static char* FontConfig_Select( FcConfig* config, const char* family,
465 bool b_bold, bool b_italic, int i_size, int *i_idx )
467 FcResult result = FcResultMatch;
468 FcPattern *pat, *p_pat;
469 FcChar8* val_s;
470 FcBool val_b;
472 /* Create a pattern and fills it */
473 pat = FcPatternCreate();
474 if (!pat) return NULL;
476 /* */
477 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
478 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
479 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
480 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
481 if( i_size != -1 )
483 char *psz_fontsize;
484 if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
486 FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
487 free( psz_fontsize );
491 /* */
492 FcDefaultSubstitute( pat );
493 if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
495 FcPatternDestroy( pat );
496 return NULL;
499 /* Find the best font for the pattern, destroy the pattern */
500 p_pat = FcFontMatch( config, pat, &result );
501 FcPatternDestroy( pat );
502 if( !p_pat || result == FcResultNoMatch ) return NULL;
504 /* Check the new pattern */
505 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
506 || ( val_b != FcTrue ) )
508 FcPatternDestroy( p_pat );
509 return NULL;
511 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
513 *i_idx = 0;
516 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
518 FcPatternDestroy( p_pat );
519 return NULL;
522 /* if( strcasecmp((const char*)val_s, family ) != 0 )
523 msg_Warn( p_filter, "fontconfig: selected font family is not"
524 "the requested one: '%s' != '%s'\n",
525 (const char*)val_s, family ); */
527 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
529 FcPatternDestroy( p_pat );
530 return NULL;
533 FcPatternDestroy( p_pat );
534 return strdup( (const char*)val_s );
536 #endif
538 #ifdef WIN32
539 #define UNICODE
540 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
542 static int GetFileFontByName( const char *font_name, char **psz_filename )
544 HKEY hKey;
545 wchar_t vbuffer[MAX_PATH];
546 wchar_t dbuffer[256];
548 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey) != ERROR_SUCCESS )
549 return 1;
551 for( int index = 0;; index++ )
553 DWORD vbuflen = MAX_PATH - 1;
554 DWORD dbuflen = 255;
556 if( RegEnumValueW( hKey, index, vbuffer, &vbuflen,
557 NULL, NULL, (LPBYTE)dbuffer, &dbuflen) != ERROR_SUCCESS )
558 return 2;
560 char *psz_value = FromWide( vbuffer );
562 char *s = strchr( psz_value,'(' );
563 if( s != NULL && s != psz_value ) s[-1] = '\0';
565 /* Manage concatenated font names */
566 if( strchr( psz_value, '&') ) {
567 if( strcasestr( psz_value, font_name ) != NULL )
568 break;
570 else {
571 if( strcasecmp( psz_value, font_name ) == 0 )
572 break;
576 *psz_filename = FromWide( dbuffer );
577 return 0;
581 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
582 DWORD type, LPARAM lParam)
584 VLC_UNUSED( metric );
585 if( (type & RASTER_FONTTYPE) ) return 1;
586 // if( lpelfe->elfScript ) FIXME
588 return GetFileFontByName( (const char *)lpelfe->elfFullName, (char **)lParam );
591 static char* Win32_Select( filter_t *p_filter, const char* family,
592 bool b_bold, bool b_italic, int i_size, int *i_idx )
594 VLC_UNUSED( i_size );
596 /* */
597 LOGFONT lf;
598 lf.lfCharSet = DEFAULT_CHARSET;
599 if( b_italic )
600 lf.lfItalic = true;
601 if( b_bold )
602 lf.lfWeight = FW_BOLD;
603 strncpy( (LPSTR)&lf.lfFaceName, family, 32);
605 /* */
606 char *psz_filename = NULL;
607 HDC hDC = GetDC( NULL );
608 EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
609 ReleaseDC(NULL, hDC);
611 /* */
612 if( psz_filename != NULL )
614 /* FIXME: increase i_idx, when concatenated strings */
615 i_idx = 0;
617 /* Prepend the Windows Font path, when only a filename was provided */
618 if( strchr( psz_filename, DIR_SEP_CHAR ) )
619 return psz_filename;
620 else
622 char *psz_tmp;
623 if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
625 free( psz_filename );
626 return NULL;
628 free( psz_filename );
629 return psz_tmp;
632 else /* Let's take any font we can */
634 char *psz_tmp;
635 if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, "arial.ttf" ) == -1 )
636 return NULL;
637 else
638 return psz_tmp;
641 #endif /* HAVE_WIN32 */
643 #endif /* HAVE_STYLES */
646 /*****************************************************************************
647 * RenderYUVP: place string in picture
648 *****************************************************************************
649 * This function merges the previously rendered freetype glyphs into a picture
650 *****************************************************************************/
651 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
652 line_desc_t *p_line,
653 FT_BBox *p_bbox )
655 VLC_UNUSED(p_filter);
656 static const uint8_t pi_gamma[16] =
657 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
658 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
660 uint8_t *p_dst;
661 video_format_t fmt;
662 int i, x, y, i_pitch;
663 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
665 /* Create a new subpicture region */
666 video_format_Init( &fmt, VLC_CODEC_YUVP );
667 fmt.i_width =
668 fmt.i_visible_width = p_bbox->xMax - p_bbox->xMin + 4;
669 fmt.i_height =
670 fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
672 assert( !p_region->p_picture );
673 p_region->p_picture = picture_NewFromFormat( &fmt );
674 if( !p_region->p_picture )
675 return VLC_EGENERIC;
676 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
677 p_region->fmt = fmt;
679 /* Calculate text color components
680 * Only use the first color */
681 int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
682 YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
684 /* Build palette */
685 fmt.p_palette->i_entries = 16;
686 for( i = 0; i < 8; i++ )
688 fmt.p_palette->palette[i][0] = 0;
689 fmt.p_palette->palette[i][1] = 0x80;
690 fmt.p_palette->palette[i][2] = 0x80;
691 fmt.p_palette->palette[i][3] = pi_gamma[i];
692 fmt.p_palette->palette[i][3] =
693 (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
695 for( i = 8; i < fmt.p_palette->i_entries; i++ )
697 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
698 fmt.p_palette->palette[i][1] = i_u;
699 fmt.p_palette->palette[i][2] = i_v;
700 fmt.p_palette->palette[i][3] = pi_gamma[i];
701 fmt.p_palette->palette[i][3] =
702 (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
705 p_dst = p_region->p_picture->Y_PIXELS;
706 i_pitch = p_region->p_picture->Y_PITCH;
708 /* Initialize the region pixels */
709 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
711 for( ; p_line != NULL; p_line = p_line->p_next )
713 int i_align_left = 0;
714 if( p_line->i_width < (int)fmt.i_visible_width )
716 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
717 i_align_left = ( fmt.i_visible_width - p_line->i_width );
718 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
719 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
721 int i_align_top = 0;
723 for( i = 0; i < p_line->i_character_count; i++ )
725 const line_character_t *ch = &p_line->p_character[i];
726 FT_BitmapGlyph p_glyph = ch->p_glyph;
728 int i_glyph_y = i_align_top - p_glyph->top + p_bbox->yMax + p_line->i_base_line;
729 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
731 for( y = 0; y < p_glyph->bitmap.rows; y++ )
733 for( x = 0; x < p_glyph->bitmap.width; x++ )
735 if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
736 p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
737 (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
743 /* Outlining (find something better than nearest neighbour filtering ?) */
744 if( 1 )
746 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
747 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
748 uint8_t left, current;
750 for( y = 1; y < (int)fmt.i_height - 1; y++ )
752 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
753 p_dst += p_region->p_picture->Y_PITCH;
754 left = 0;
756 for( x = 1; x < (int)fmt.i_width - 1; x++ )
758 current = p_dst[x];
759 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
760 p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
761 left = current;
764 memset( p_top, 0, fmt.i_width );
767 return VLC_SUCCESS;
770 /*****************************************************************************
771 * RenderYUVA: place string in picture
772 *****************************************************************************
773 * This function merges the previously rendered freetype glyphs into a picture
774 *****************************************************************************/
775 static void FillYUVAPicture( picture_t *p_picture,
776 int i_a, int i_y, int i_u, int i_v )
778 memset( p_picture->p[0].p_pixels, i_y,
779 p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
780 memset( p_picture->p[1].p_pixels, i_u,
781 p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
782 memset( p_picture->p[2].p_pixels, i_v,
783 p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
784 memset( p_picture->p[3].p_pixels, i_a,
785 p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
788 static inline void BlendYUVAPixel( picture_t *p_picture,
789 int i_picture_x, int i_picture_y,
790 int i_a, int i_y, int i_u, int i_v,
791 int i_alpha )
793 int i_an = i_a * i_alpha / 255;
795 uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
796 uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
797 uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
798 uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
800 int i_ao = *p_a;
801 if( i_ao == 0 )
803 *p_y = i_y;
804 *p_u = i_u;
805 *p_v = i_v;
806 *p_a = i_an;
808 else
810 *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
811 if( *p_a != 0 )
813 *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
814 *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
815 *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
820 static void FillRGBAPicture( picture_t *p_picture,
821 int i_a, int i_r, int i_g, int i_b )
823 for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
825 for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
827 uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
828 p_rgba[0] = i_r;
829 p_rgba[1] = i_g;
830 p_rgba[2] = i_b;
831 p_rgba[3] = i_a;
836 static inline void BlendRGBAPixel( picture_t *p_picture,
837 int i_picture_x, int i_picture_y,
838 int i_a, int i_r, int i_g, int i_b,
839 int i_alpha )
841 int i_an = i_a * i_alpha / 255;
843 uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
845 int i_ao = p_rgba[3];
846 if( i_ao == 0 )
848 p_rgba[0] = i_r;
849 p_rgba[1] = i_g;
850 p_rgba[2] = i_b;
851 p_rgba[3] = i_an;
853 else
855 p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
856 if( p_rgba[3] != 0 )
858 p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
859 p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
860 p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
865 static inline void BlendAXYZGlyph( picture_t *p_picture,
866 int i_picture_x, int i_picture_y,
867 int i_a, int i_x, int i_y, int i_z,
868 FT_BitmapGlyph p_glyph,
869 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
872 for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
874 for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
875 BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
876 i_a, i_x, i_y, i_z,
877 p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
881 static inline void BlendAXYZLine( picture_t *p_picture,
882 int i_picture_x, int i_picture_y,
883 int i_a, int i_x, int i_y, int i_z,
884 const line_character_t *p_current,
885 const line_character_t *p_next,
886 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
888 int i_line_width = p_current->p_glyph->bitmap.width;
889 if( p_next )
890 i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
892 for( int dx = 0; dx < i_line_width; dx++ )
894 for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
895 BlendPixel( p_picture,
896 i_picture_x + dx,
897 i_picture_y + p_current->i_line_offset + dy,
898 i_a, i_x, i_y, i_z, 0xff );
902 static inline int RenderAXYZ( filter_t *p_filter,
903 subpicture_region_t *p_region,
904 line_desc_t *p_line_head,
905 FT_BBox *p_bbox,
906 int i_margin,
907 vlc_fourcc_t i_chroma,
908 void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
909 void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
910 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
912 filter_sys_t *p_sys = p_filter->p_sys;
914 /* Create a new subpicture region */
915 const int i_text_width = p_bbox->xMax - p_bbox->xMin;
916 const int i_text_height = p_bbox->yMax - p_bbox->yMin;
917 video_format_t fmt;
918 video_format_Init( &fmt, i_chroma );
919 fmt.i_width =
920 fmt.i_visible_width = i_text_width + 2 * i_margin;
921 fmt.i_height =
922 fmt.i_visible_height = i_text_height + 2 * i_margin;
924 picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
925 if( !p_region->p_picture )
926 return VLC_EGENERIC;
927 p_region->fmt = fmt;
929 /* Initialize the picture background */
930 uint8_t i_a = p_sys->i_background_opacity;
931 uint8_t i_x, i_y, i_z;
932 ExtractComponents( p_sys->i_background_color, &i_x, &i_y, &i_z );
934 FillPicture( p_picture, i_a, i_x, i_y, i_z );
936 /* Render shadow then outline and then normal glyphs */
937 for( int g = 0; g < 3; g++ )
939 /* Render all lines */
940 for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
942 int i_align_left = i_margin;
943 if( p_line->i_width < i_text_width )
945 /* Left offset to take into account alignment */
946 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
947 i_align_left += ( i_text_width - p_line->i_width );
948 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
949 i_align_left += ( i_text_width - p_line->i_width ) / 2;
951 int i_align_top = i_margin;
953 /* Render all glyphs and underline/strikethrough */
954 for( int i = 0; i < p_line->i_character_count; i++ )
956 const line_character_t *ch = &p_line->p_character[i];
957 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
958 if( !p_glyph )
959 continue;
961 i_a = (ch->i_color >> 24) & 0xff;
962 uint32_t i_color;
963 switch (g) {
964 case 0:
965 i_a = i_a * p_sys->i_shadow_opacity / 255;
966 i_color = p_sys->i_shadow_color;
967 break;
968 case 1:
969 i_a = i_a * p_sys->i_outline_opacity / 255;
970 i_color = p_sys->i_outline_color;
971 break;
972 default:
973 i_color = ch->i_color;
974 break;
976 ExtractComponents( i_color, &i_x, &i_y, &i_z );
978 int i_glyph_y = i_align_top - p_glyph->top + p_bbox->yMax + p_line->i_base_line;
979 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
981 BlendAXYZGlyph( p_picture,
982 i_glyph_x, i_glyph_y,
983 i_a, i_x, i_y, i_z,
984 p_glyph,
985 BlendPixel );
987 /* underline/strikethrough are only rendered for the normal glyph */
988 if( g == 2 && ch->i_line_thickness > 0 )
989 BlendAXYZLine( p_picture,
990 i_glyph_x, i_glyph_y + p_glyph->top,
991 i_a, i_x, i_y, i_z,
992 &ch[0],
993 i + 1 < p_line->i_character_count ? &ch[1] : NULL,
994 BlendPixel );
999 return VLC_SUCCESS;
1002 static text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1003 uint32_t i_font_color, uint32_t i_karaoke_bg_color,
1004 int i_style_flags )
1006 text_style_t *p_style = text_style_New();
1007 if( !p_style )
1008 return NULL;
1010 p_style->psz_fontname = psz_fontname ? strdup( psz_fontname ) : NULL;
1011 p_style->i_font_size = i_font_size;
1012 p_style->i_font_color = (i_font_color & 0x00ffffff) >> 0;
1013 p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
1014 p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >> 0;
1015 p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
1016 p_style->i_style_flags |= i_style_flags;
1017 return p_style;
1020 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1021 uint32_t i_color, uint32_t i_karaoke_bg_color )
1023 if( !p_font )
1024 return VLC_EGENERIC;
1026 font_stack_t *p_new = malloc( sizeof(*p_new) );
1027 if( !p_new )
1028 return VLC_ENOMEM;
1030 p_new->p_next = NULL;
1032 if( psz_name )
1033 p_new->psz_name = strdup( psz_name );
1034 else
1035 p_new->psz_name = NULL;
1037 p_new->i_size = i_size;
1038 p_new->i_color = i_color;
1039 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1041 if( !*p_font )
1043 *p_font = p_new;
1045 else
1047 font_stack_t *p_last;
1049 for( p_last = *p_font;
1050 p_last->p_next;
1051 p_last = p_last->p_next )
1054 p_last->p_next = p_new;
1056 return VLC_SUCCESS;
1059 static int PopFont( font_stack_t **p_font )
1061 font_stack_t *p_last, *p_next_to_last;
1063 if( !p_font || !*p_font )
1064 return VLC_EGENERIC;
1066 p_next_to_last = NULL;
1067 for( p_last = *p_font;
1068 p_last->p_next;
1069 p_last = p_last->p_next )
1071 p_next_to_last = p_last;
1074 if( p_next_to_last )
1075 p_next_to_last->p_next = NULL;
1076 else
1077 *p_font = NULL;
1079 free( p_last->psz_name );
1080 free( p_last );
1082 return VLC_SUCCESS;
1085 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1086 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1088 font_stack_t *p_last;
1090 if( !p_font || !*p_font )
1091 return VLC_EGENERIC;
1093 for( p_last=*p_font;
1094 p_last->p_next;
1095 p_last=p_last->p_next )
1098 *psz_name = p_last->psz_name;
1099 *i_size = p_last->i_size;
1100 *i_color = p_last->i_color;
1101 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1103 return VLC_SUCCESS;
1106 static const struct {
1107 const char *psz_name;
1108 uint32_t i_value;
1109 } p_html_colors[] = {
1110 /* Official html colors */
1111 { "Aqua", 0x00FFFF },
1112 { "Black", 0x000000 },
1113 { "Blue", 0x0000FF },
1114 { "Fuchsia", 0xFF00FF },
1115 { "Gray", 0x808080 },
1116 { "Green", 0x008000 },
1117 { "Lime", 0x00FF00 },
1118 { "Maroon", 0x800000 },
1119 { "Navy", 0x000080 },
1120 { "Olive", 0x808000 },
1121 { "Purple", 0x800080 },
1122 { "Red", 0xFF0000 },
1123 { "Silver", 0xC0C0C0 },
1124 { "Teal", 0x008080 },
1125 { "White", 0xFFFFFF },
1126 { "Yellow", 0xFFFF00 },
1128 /* Common ones */
1129 { "AliceBlue", 0xF0F8FF },
1130 { "AntiqueWhite", 0xFAEBD7 },
1131 { "Aqua", 0x00FFFF },
1132 { "Aquamarine", 0x7FFFD4 },
1133 { "Azure", 0xF0FFFF },
1134 { "Beige", 0xF5F5DC },
1135 { "Bisque", 0xFFE4C4 },
1136 { "Black", 0x000000 },
1137 { "BlanchedAlmond", 0xFFEBCD },
1138 { "Blue", 0x0000FF },
1139 { "BlueViolet", 0x8A2BE2 },
1140 { "Brown", 0xA52A2A },
1141 { "BurlyWood", 0xDEB887 },
1142 { "CadetBlue", 0x5F9EA0 },
1143 { "Chartreuse", 0x7FFF00 },
1144 { "Chocolate", 0xD2691E },
1145 { "Coral", 0xFF7F50 },
1146 { "CornflowerBlue", 0x6495ED },
1147 { "Cornsilk", 0xFFF8DC },
1148 { "Crimson", 0xDC143C },
1149 { "Cyan", 0x00FFFF },
1150 { "DarkBlue", 0x00008B },
1151 { "DarkCyan", 0x008B8B },
1152 { "DarkGoldenRod", 0xB8860B },
1153 { "DarkGray", 0xA9A9A9 },
1154 { "DarkGrey", 0xA9A9A9 },
1155 { "DarkGreen", 0x006400 },
1156 { "DarkKhaki", 0xBDB76B },
1157 { "DarkMagenta", 0x8B008B },
1158 { "DarkOliveGreen", 0x556B2F },
1159 { "Darkorange", 0xFF8C00 },
1160 { "DarkOrchid", 0x9932CC },
1161 { "DarkRed", 0x8B0000 },
1162 { "DarkSalmon", 0xE9967A },
1163 { "DarkSeaGreen", 0x8FBC8F },
1164 { "DarkSlateBlue", 0x483D8B },
1165 { "DarkSlateGray", 0x2F4F4F },
1166 { "DarkSlateGrey", 0x2F4F4F },
1167 { "DarkTurquoise", 0x00CED1 },
1168 { "DarkViolet", 0x9400D3 },
1169 { "DeepPink", 0xFF1493 },
1170 { "DeepSkyBlue", 0x00BFFF },
1171 { "DimGray", 0x696969 },
1172 { "DimGrey", 0x696969 },
1173 { "DodgerBlue", 0x1E90FF },
1174 { "FireBrick", 0xB22222 },
1175 { "FloralWhite", 0xFFFAF0 },
1176 { "ForestGreen", 0x228B22 },
1177 { "Fuchsia", 0xFF00FF },
1178 { "Gainsboro", 0xDCDCDC },
1179 { "GhostWhite", 0xF8F8FF },
1180 { "Gold", 0xFFD700 },
1181 { "GoldenRod", 0xDAA520 },
1182 { "Gray", 0x808080 },
1183 { "Grey", 0x808080 },
1184 { "Green", 0x008000 },
1185 { "GreenYellow", 0xADFF2F },
1186 { "HoneyDew", 0xF0FFF0 },
1187 { "HotPink", 0xFF69B4 },
1188 { "IndianRed", 0xCD5C5C },
1189 { "Indigo", 0x4B0082 },
1190 { "Ivory", 0xFFFFF0 },
1191 { "Khaki", 0xF0E68C },
1192 { "Lavender", 0xE6E6FA },
1193 { "LavenderBlush", 0xFFF0F5 },
1194 { "LawnGreen", 0x7CFC00 },
1195 { "LemonChiffon", 0xFFFACD },
1196 { "LightBlue", 0xADD8E6 },
1197 { "LightCoral", 0xF08080 },
1198 { "LightCyan", 0xE0FFFF },
1199 { "LightGoldenRodYellow", 0xFAFAD2 },
1200 { "LightGray", 0xD3D3D3 },
1201 { "LightGrey", 0xD3D3D3 },
1202 { "LightGreen", 0x90EE90 },
1203 { "LightPink", 0xFFB6C1 },
1204 { "LightSalmon", 0xFFA07A },
1205 { "LightSeaGreen", 0x20B2AA },
1206 { "LightSkyBlue", 0x87CEFA },
1207 { "LightSlateGray", 0x778899 },
1208 { "LightSlateGrey", 0x778899 },
1209 { "LightSteelBlue", 0xB0C4DE },
1210 { "LightYellow", 0xFFFFE0 },
1211 { "Lime", 0x00FF00 },
1212 { "LimeGreen", 0x32CD32 },
1213 { "Linen", 0xFAF0E6 },
1214 { "Magenta", 0xFF00FF },
1215 { "Maroon", 0x800000 },
1216 { "MediumAquaMarine", 0x66CDAA },
1217 { "MediumBlue", 0x0000CD },
1218 { "MediumOrchid", 0xBA55D3 },
1219 { "MediumPurple", 0x9370D8 },
1220 { "MediumSeaGreen", 0x3CB371 },
1221 { "MediumSlateBlue", 0x7B68EE },
1222 { "MediumSpringGreen", 0x00FA9A },
1223 { "MediumTurquoise", 0x48D1CC },
1224 { "MediumVioletRed", 0xC71585 },
1225 { "MidnightBlue", 0x191970 },
1226 { "MintCream", 0xF5FFFA },
1227 { "MistyRose", 0xFFE4E1 },
1228 { "Moccasin", 0xFFE4B5 },
1229 { "NavajoWhite", 0xFFDEAD },
1230 { "Navy", 0x000080 },
1231 { "OldLace", 0xFDF5E6 },
1232 { "Olive", 0x808000 },
1233 { "OliveDrab", 0x6B8E23 },
1234 { "Orange", 0xFFA500 },
1235 { "OrangeRed", 0xFF4500 },
1236 { "Orchid", 0xDA70D6 },
1237 { "PaleGoldenRod", 0xEEE8AA },
1238 { "PaleGreen", 0x98FB98 },
1239 { "PaleTurquoise", 0xAFEEEE },
1240 { "PaleVioletRed", 0xD87093 },
1241 { "PapayaWhip", 0xFFEFD5 },
1242 { "PeachPuff", 0xFFDAB9 },
1243 { "Peru", 0xCD853F },
1244 { "Pink", 0xFFC0CB },
1245 { "Plum", 0xDDA0DD },
1246 { "PowderBlue", 0xB0E0E6 },
1247 { "Purple", 0x800080 },
1248 { "Red", 0xFF0000 },
1249 { "RosyBrown", 0xBC8F8F },
1250 { "RoyalBlue", 0x4169E1 },
1251 { "SaddleBrown", 0x8B4513 },
1252 { "Salmon", 0xFA8072 },
1253 { "SandyBrown", 0xF4A460 },
1254 { "SeaGreen", 0x2E8B57 },
1255 { "SeaShell", 0xFFF5EE },
1256 { "Sienna", 0xA0522D },
1257 { "Silver", 0xC0C0C0 },
1258 { "SkyBlue", 0x87CEEB },
1259 { "SlateBlue", 0x6A5ACD },
1260 { "SlateGray", 0x708090 },
1261 { "SlateGrey", 0x708090 },
1262 { "Snow", 0xFFFAFA },
1263 { "SpringGreen", 0x00FF7F },
1264 { "SteelBlue", 0x4682B4 },
1265 { "Tan", 0xD2B48C },
1266 { "Teal", 0x008080 },
1267 { "Thistle", 0xD8BFD8 },
1268 { "Tomato", 0xFF6347 },
1269 { "Turquoise", 0x40E0D0 },
1270 { "Violet", 0xEE82EE },
1271 { "Wheat", 0xF5DEB3 },
1272 { "White", 0xFFFFFF },
1273 { "WhiteSmoke", 0xF5F5F5 },
1274 { "Yellow", 0xFFFF00 },
1275 { "YellowGreen", 0x9ACD32 },
1277 { NULL, 0 }
1280 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1281 font_stack_t **p_fonts )
1283 int rv;
1284 char *psz_fontname = NULL;
1285 uint32_t i_font_color = 0xffffff;
1286 int i_font_alpha = 255;
1287 uint32_t i_karaoke_bg_color = 0x00ffffff;
1288 int i_font_size = 24;
1290 /* Default all attributes to the top font in the stack -- in case not
1291 * all attributes are specified in the sub-font
1293 if( VLC_SUCCESS == PeekFont( p_fonts,
1294 &psz_fontname,
1295 &i_font_size,
1296 &i_font_color,
1297 &i_karaoke_bg_color ))
1299 psz_fontname = strdup( psz_fontname );
1300 i_font_size = i_font_size;
1302 i_font_alpha = (i_font_color >> 24) & 0xff;
1303 i_font_color &= 0x00ffffff;
1305 const char *name, *value;
1306 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1308 if( !strcasecmp( "face", name ) )
1310 free( psz_fontname );
1311 psz_fontname = strdup( value );
1313 else if( !strcasecmp( "size", name ) )
1315 if( ( *value == '+' ) || ( *value == '-' ) )
1317 int i_value = atoi( value );
1319 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1320 i_font_size += ( i_value * i_font_size ) / 10;
1321 else if( i_value < -5 )
1322 i_font_size = - i_value;
1323 else if( i_value > 5 )
1324 i_font_size = i_value;
1326 else
1327 i_font_size = atoi( value );
1329 else if( !strcasecmp( "color", name ) )
1331 if( value[0] == '#' )
1333 i_font_color = strtol( value + 1, NULL, 16 );
1334 i_font_color &= 0x00ffffff;
1336 else
1338 char *end;
1339 uint32_t i_value = strtol( value, &end, 16 );
1340 if( *end == '\0' || *end == ' ' )
1341 i_font_color = i_value & 0x00ffffff;
1343 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
1345 if( !strncasecmp( value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
1347 i_font_color = p_html_colors[i].i_value;
1348 break;
1353 else if( !strcasecmp( "alpha", name ) && ( value[0] == '#' ) )
1355 i_font_alpha = strtol( value + 1, NULL, 16 );
1356 i_font_alpha &= 0xff;
1359 rv = PushFont( p_fonts,
1360 psz_fontname,
1361 i_font_size,
1362 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1363 i_karaoke_bg_color );
1365 free( psz_fontname );
1367 return rv;
1370 /* Turn any multiple-whitespaces into single spaces */
1371 static void HandleWhiteSpace( char *psz_node )
1373 char *s = strpbrk( psz_node, "\t\r\n " );
1374 while( s )
1376 int i_whitespace = strspn( s, "\t\r\n " );
1378 if( i_whitespace > 1 )
1379 memmove( &s[1],
1380 &s[i_whitespace],
1381 strlen( s ) - i_whitespace + 1 );
1382 *s++ = ' ';
1384 s = strpbrk( s, "\t\r\n " );
1389 static text_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1390 font_stack_t **p_fonts,
1391 int i_style_flags )
1393 char *psz_fontname = NULL;
1394 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1395 uint32_t i_karaoke_bg_color = i_font_color;
1396 int i_font_size = p_sys->i_font_size;
1398 if( PeekFont( p_fonts, &psz_fontname, &i_font_size,
1399 &i_font_color, &i_karaoke_bg_color ) )
1400 return NULL;
1402 return CreateStyle( psz_fontname, i_font_size, i_font_color,
1403 i_karaoke_bg_color,
1404 i_style_flags );
1407 static unsigned SetupText( filter_t *p_filter,
1408 uint32_t *psz_text_out,
1409 text_style_t **pp_styles,
1410 uint32_t *pi_k_dates,
1412 const char *psz_text_in,
1413 text_style_t *p_style,
1414 uint32_t i_k_date )
1416 size_t i_string_length;
1418 size_t i_string_bytes;
1419 #if defined(WORDS_BIGENDIAN)
1420 uint32_t *psz_tmp = ToCharset( "UCS-4BE", psz_text_in, &i_string_bytes );
1421 #else
1422 uint32_t *psz_tmp = ToCharset( "UCS-4LE", psz_text_in, &i_string_bytes );
1423 #endif
1424 if( psz_tmp )
1426 memcpy( psz_text_out, psz_tmp, i_string_bytes );
1427 i_string_length = i_string_bytes / 4;
1428 free( psz_tmp );
1430 else
1432 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1433 i_string_length = 0;
1436 if( i_string_length > 0 )
1438 for( unsigned i = 0; i < i_string_length; i++ )
1439 pp_styles[i] = p_style;
1441 else
1443 text_style_Delete( p_style );
1445 if( i_string_length > 0 && pi_k_dates )
1447 for( unsigned i = 0; i < i_string_length; i++ )
1448 pi_k_dates[i] = i_k_date;
1450 return i_string_length;
1453 static int ProcessNodes( filter_t *p_filter,
1454 uint32_t *psz_text,
1455 text_style_t **pp_styles,
1456 uint32_t *pi_k_dates,
1457 int *pi_len,
1458 xml_reader_t *p_xml_reader,
1459 text_style_t *p_font_style )
1461 int rv = VLC_SUCCESS;
1462 filter_sys_t *p_sys = p_filter->p_sys;
1463 int i_text_length = 0;
1464 font_stack_t *p_fonts = NULL;
1465 uint32_t i_k_date = 0;
1467 int i_style_flags = 0;
1469 if( p_font_style )
1471 rv = PushFont( &p_fonts,
1472 p_font_style->psz_fontname,
1473 p_font_style->i_font_size > 0 ? p_font_style->i_font_size
1474 : p_sys->i_font_size,
1475 (p_font_style->i_font_color & 0xffffff) |
1476 ((p_font_style->i_font_alpha & 0xff) << 24),
1477 (p_font_style->i_karaoke_background_color & 0xffffff) |
1478 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1480 i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
1481 STYLE_ITALIC |
1482 STYLE_UNDERLINE |
1483 STYLE_STRIKEOUT);
1485 #ifdef HAVE_STYLES
1486 else
1488 rv = PushFont( &p_fonts,
1489 p_sys->psz_fontfamily,
1490 p_sys->i_font_size,
1491 (p_sys->i_font_color & 0xffffff) |
1492 ((p_sys->i_font_opacity & 0xff) << 24),
1493 0x00ffffff );
1495 #endif
1496 if( p_sys->b_font_bold )
1497 i_style_flags |= STYLE_BOLD;
1499 if( rv != VLC_SUCCESS )
1500 return rv;
1502 const char *node;
1503 int type;
1505 while ( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
1507 switch ( type )
1509 case XML_READER_ENDELEM:
1510 if( !strcasecmp( "font", node ) )
1511 PopFont( &p_fonts );
1512 else if( !strcasecmp( "b", node ) )
1513 i_style_flags &= ~STYLE_BOLD;
1514 else if( !strcasecmp( "i", node ) )
1515 i_style_flags &= ~STYLE_ITALIC;
1516 else if( !strcasecmp( "u", node ) )
1517 i_style_flags &= ~STYLE_UNDERLINE;
1518 else if( !strcasecmp( "s", node ) )
1519 i_style_flags &= ~STYLE_STRIKEOUT;
1520 break;
1522 case XML_READER_STARTELEM:
1523 if( !strcasecmp( "font", node ) )
1524 HandleFontAttributes( p_xml_reader, &p_fonts );
1525 else if( !strcasecmp( "b", node ) )
1526 i_style_flags |= STYLE_BOLD;
1527 else if( !strcasecmp( "i", node ) )
1528 i_style_flags |= STYLE_ITALIC;
1529 else if( !strcasecmp( "u", node ) )
1530 i_style_flags |= STYLE_UNDERLINE;
1531 else if( !strcasecmp( "s", node ) )
1532 i_style_flags |= STYLE_STRIKEOUT;
1533 else if( !strcasecmp( "br", node ) )
1535 i_text_length += SetupText( p_filter,
1536 &psz_text[i_text_length],
1537 &pp_styles[i_text_length],
1538 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1539 "\n",
1540 GetStyleFromFontStack( p_sys,
1541 &p_fonts,
1542 i_style_flags ),
1543 i_k_date );
1545 else if( !strcasecmp( "k", node ) )
1547 /* Karaoke tags */
1548 const char *name, *value;
1549 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1551 if( !strcasecmp( "t", name ) && value )
1552 i_k_date += atoi( value );
1555 break;
1557 case XML_READER_TEXT:
1559 char *psz_node = strdup( node );
1560 if( unlikely(!psz_node) )
1561 break;
1563 HandleWhiteSpace( psz_node );
1564 resolve_xml_special_chars( psz_node );
1566 i_text_length += SetupText( p_filter,
1567 &psz_text[i_text_length],
1568 &pp_styles[i_text_length],
1569 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1570 psz_node,
1571 GetStyleFromFontStack( p_sys,
1572 &p_fonts,
1573 i_style_flags ),
1574 i_k_date );
1575 free( psz_node );
1576 break;
1581 *pi_len = i_text_length;
1583 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1585 return VLC_SUCCESS;
1588 static void FreeLine( line_desc_t *p_line )
1590 for( int i = 0; i < p_line->i_character_count; i++ )
1592 line_character_t *ch = &p_line->p_character[i];
1593 FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1594 if( ch->p_outline )
1595 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1596 if( ch->p_shadow )
1597 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1600 free( p_line->p_character );
1601 free( p_line );
1604 static void FreeLines( line_desc_t *p_lines )
1606 for( line_desc_t *p_line = p_lines; p_line != NULL; )
1608 line_desc_t *p_next = p_line->p_next;
1609 FreeLine( p_line );
1610 p_line = p_next;
1614 static line_desc_t *NewLine( int i_count )
1616 line_desc_t *p_line = malloc( sizeof(*p_line) );
1618 if( !p_line )
1619 return NULL;
1621 p_line->p_next = NULL;
1622 p_line->i_width = 0;
1623 p_line->i_base_line = 0;
1624 p_line->i_character_count = 0;
1626 p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
1627 if( !p_line->p_character )
1629 free( p_line );
1630 return NULL;
1632 return p_line;
1635 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1637 for( int k = 0; k < p_sys->i_font_attachments; k++ )
1639 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1640 int i_font_idx = 0;
1641 FT_Face p_face = NULL;
1643 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1644 p_attach->p_data,
1645 p_attach->i_data,
1646 i_font_idx,
1647 &p_face ))
1649 if( p_face )
1651 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD) ? STYLE_BOLD : 0) |
1652 ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1653 if( !strcasecmp( p_face->family_name, p_style->psz_fontname ) &&
1654 (p_style->i_style_flags & (STYLE_BOLD | STYLE_BOLD)) == i_style_received )
1655 return p_face;
1657 FT_Done_Face( p_face );
1659 i_font_idx++;
1662 return NULL;
1665 static FT_Face LoadFace( filter_t *p_filter,
1666 const text_style_t *p_style )
1668 filter_sys_t *p_sys = p_filter->p_sys;
1670 /* Look for a match amongst our attachments first */
1671 FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1673 /* Load system wide font otheriwse */
1674 if( !p_face )
1676 int i_idx = 0;
1677 char *psz_fontfile;
1678 #ifdef HAVE_FONTCONFIG
1679 psz_fontfile = FontConfig_Select( NULL,
1680 p_style->psz_fontname,
1681 (p_style->i_style_flags & STYLE_BOLD) != 0,
1682 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1684 &i_idx );
1685 #elif defined( WIN32 )
1686 psz_fontfile = Win32_Select( p_filter,
1687 p_style->psz_fontname,
1688 (p_style->i_style_flags & STYLE_BOLD) != 0,
1689 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1691 &i_idx );
1692 #else
1693 psz_fontfile = NULL;
1694 #endif
1695 if( !psz_fontfile )
1696 return NULL;
1698 if( *psz_fontfile == '\0' )
1700 msg_Warn( p_filter,
1701 "We were not able to find a matching font: \"%s\" (%s %s),"
1702 " so using default font",
1703 p_style->psz_fontname,
1704 (p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
1705 (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1706 p_face = NULL;
1708 else
1710 if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1711 p_face = NULL;
1713 free( psz_fontfile );
1715 if( !p_face )
1716 return NULL;
1718 if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1720 /* We've loaded a font face which is unhelpful for actually
1721 * rendering text - fallback to the default one.
1723 FT_Done_Face( p_face );
1724 return NULL;
1726 return p_face;
1729 static bool FaceStyleEquals( const text_style_t *p_style1,
1730 const text_style_t *p_style2 )
1732 if( !p_style1 || !p_style2 )
1733 return false;
1734 if( p_style1 == p_style2 )
1735 return true;
1737 const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
1738 return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
1739 !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
1742 static int GetGlyph( filter_t *p_filter,
1743 FT_Glyph *pp_glyph, FT_BBox *p_glyph_bbox,
1744 FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
1745 FT_Glyph *pp_shadow, FT_BBox *p_shadow_bbox,
1747 FT_Face p_face,
1748 int i_glyph_index,
1749 int i_style_flags,
1750 FT_Vector *p_pen,
1751 FT_Vector *p_pen_shadow )
1753 if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
1754 FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
1756 msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
1757 return VLC_EGENERIC;
1760 /* Do synthetic styling now that Freetype supports it;
1761 * ie. if the font we have loaded is NOT already in the
1762 * style that the tags want, then switch it on; if they
1763 * are then don't. */
1764 if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1765 FT_GlyphSlot_Embolden( p_face->glyph );
1766 if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1767 FT_GlyphSlot_Oblique( p_face->glyph );
1769 FT_Glyph glyph;
1770 if( FT_Get_Glyph( p_face->glyph, &glyph ) )
1772 msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
1773 return VLC_EGENERIC;
1776 FT_Glyph outline = NULL;
1777 if( p_filter->p_sys->p_stroker )
1779 outline = glyph;
1780 if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
1781 outline = NULL;
1784 FT_Glyph shadow = NULL;
1785 if( p_filter->p_sys->i_shadow_opacity > 0 )
1787 shadow = outline ? outline : glyph;
1788 if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0 ) )
1790 shadow = NULL;
1792 else
1794 FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
1797 *pp_shadow = shadow;
1799 if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
1801 FT_Done_Glyph( glyph );
1802 if( outline )
1803 FT_Done_Glyph( outline );
1804 if( shadow )
1805 FT_Done_Glyph( shadow );
1806 return VLC_EGENERIC;
1808 FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
1809 *pp_glyph = glyph;
1811 if( outline )
1813 FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
1814 FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
1816 *pp_outline = outline;
1818 return VLC_SUCCESS;
1821 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
1823 FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
1824 if( p_bbox->xMin >= p_bbox->xMax )
1826 p_bbox->xMin = FT_CEIL(p_pen->x);
1827 p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
1828 glyph_bmp->left = p_bbox->xMin;
1830 if( p_bbox->yMin >= p_bbox->yMax )
1832 p_bbox->yMax = FT_CEIL(p_pen->y);
1833 p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
1834 glyph_bmp->top = p_bbox->yMax;
1838 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
1840 p_max->xMin = __MIN(p_max->xMin, p->xMin);
1841 p_max->yMin = __MIN(p_max->yMin, p->yMin);
1842 p_max->xMax = __MAX(p_max->xMax, p->xMax);
1843 p_max->yMax = __MAX(p_max->yMax, p->yMax);
1846 static int ProcessLines( filter_t *p_filter,
1847 line_desc_t **pp_lines,
1848 FT_BBox *p_bbox,
1849 int *pi_max_face_height,
1851 uint32_t *psz_text,
1852 text_style_t **pp_styles,
1853 uint32_t *pi_k_dates,
1854 int i_len )
1856 filter_sys_t *p_sys = p_filter->p_sys;
1857 uint32_t *p_fribidi_string = NULL;
1858 text_style_t **pp_fribidi_styles = NULL;
1859 int *p_new_positions = NULL;
1861 #if defined(HAVE_FRIBIDI)
1863 int *p_old_positions;
1864 int start_pos, pos = 0;
1866 pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1868 p_fribidi_string = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1869 p_old_positions = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1870 p_new_positions = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1872 if( ! pp_fribidi_styles ||
1873 ! p_fribidi_string ||
1874 ! p_old_positions ||
1875 ! p_new_positions )
1877 free( p_old_positions );
1878 free( p_new_positions );
1879 free( p_fribidi_string );
1880 free( pp_fribidi_styles );
1881 return VLC_ENOMEM;
1884 /* Do bidi conversion line-by-line */
1885 while(pos < i_len)
1887 while(pos < i_len) {
1888 if (psz_text[pos] != '\n')
1889 break;
1890 p_fribidi_string[pos] = psz_text[pos];
1891 pp_fribidi_styles[pos] = pp_styles[pos];
1892 p_new_positions[pos] = pos;
1893 ++pos;
1895 start_pos = pos;
1896 while(pos < i_len) {
1897 if (psz_text[pos] == '\n')
1898 break;
1899 ++pos;
1901 if (pos > start_pos)
1903 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1904 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1905 #else
1906 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1907 #endif
1908 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1909 pos - start_pos, &base_dir,
1910 (FriBidiChar*)p_fribidi_string + start_pos,
1911 p_new_positions + start_pos,
1912 p_old_positions,
1913 NULL );
1914 for( int j = start_pos; j < pos; j++ )
1916 pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
1917 p_new_positions[ j ] += start_pos;
1921 p_fribidi_string[ i_len ] = 0;
1922 free( p_old_positions );
1924 pp_styles = pp_fribidi_styles;
1925 psz_text = p_fribidi_string;
1927 #endif
1928 /* Work out the karaoke */
1929 uint8_t *pi_karaoke_bar = NULL;
1930 if( pi_k_dates )
1932 pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1933 if( pi_karaoke_bar )
1935 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1936 for( int i = 0; i < i_len; i++ )
1938 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
1939 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
1943 free( p_new_positions );
1945 *pi_max_face_height = 0;
1946 *pp_lines = NULL;
1947 line_desc_t **pp_line_next = pp_lines;
1949 FT_BBox bbox = {
1950 .xMin = INT_MAX,
1951 .yMin = INT_MAX,
1952 .xMax = INT_MIN,
1953 .yMax = INT_MIN,
1955 int i_face_height_previous = 0;
1956 int i_base_line = 0;
1957 const text_style_t *p_previous_style = NULL;
1958 FT_Face p_face = NULL;
1959 for( int i_start = 0; i_start < i_len; )
1961 /* Compute the length of the current text line */
1962 int i_length = 0;
1963 while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
1964 i_length++;
1966 /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
1967 line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
1968 int i_index = i_start;
1969 FT_Vector pen = {
1970 .x = 0,
1971 .y = 0,
1973 int i_face_height = 0;
1974 FT_BBox line_bbox = {
1975 .xMin = INT_MAX,
1976 .yMin = INT_MAX,
1977 .xMax = INT_MIN,
1978 .yMax = INT_MIN,
1980 int i_ul_offset = 0;
1981 int i_ul_thickness = 0;
1982 typedef struct {
1983 int i_index;
1984 FT_Vector pen;
1985 FT_BBox line_bbox;
1986 int i_face_height;
1987 int i_ul_offset;
1988 int i_ul_thickness;
1989 } break_point_t;
1990 break_point_t break_point;
1991 break_point_t break_point_fallback;
1993 #define SAVE_BP(dst) do { \
1994 dst.i_index = i_index; \
1995 dst.pen = pen; \
1996 dst.line_bbox = line_bbox; \
1997 dst.i_face_height = i_face_height; \
1998 dst.i_ul_offset = i_ul_offset; \
1999 dst.i_ul_thickness = i_ul_thickness; \
2000 } while(0)
2002 SAVE_BP( break_point );
2003 SAVE_BP( break_point_fallback );
2005 while( i_index < i_start + i_length )
2007 /* Split by common FT_Face + Size */
2008 const text_style_t *p_current_style = pp_styles[i_index];
2009 int i_part_length = 0;
2010 while( i_index + i_part_length < i_start + i_length )
2012 const text_style_t *p_style = pp_styles[i_index + i_part_length];
2013 if( !FaceStyleEquals( p_style, p_current_style ) ||
2014 p_style->i_font_size != p_current_style->i_font_size )
2015 break;
2016 i_part_length++;
2019 /* (Re)load/reconfigure the face if needed */
2020 if( !FaceStyleEquals( p_current_style, p_previous_style ) )
2022 if( p_face )
2023 FT_Done_Face( p_face );
2024 p_previous_style = NULL;
2026 p_face = LoadFace( p_filter, p_current_style );
2028 FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
2029 if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
2031 if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
2032 msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
2033 if( p_sys->p_stroker )
2035 int i_radius = (p_current_style->i_font_size << 6) * p_sys->f_outline_thickness;
2036 FT_Stroker_Set( p_sys->p_stroker,
2037 i_radius,
2038 FT_STROKER_LINECAP_ROUND,
2039 FT_STROKER_LINEJOIN_ROUND, 0 );
2042 p_previous_style = p_current_style;
2044 i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
2045 p_current_face->size->metrics.y_scale)));
2047 /* Render the part */
2048 bool b_break_line = false;
2049 int i_glyph_last = 0;
2050 while( i_part_length > 0 )
2052 const text_style_t *p_glyph_style = pp_styles[i_index];
2053 uint32_t character = psz_text[i_index];
2054 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
2056 /* Get kerning vector */
2057 FT_Vector kerning = { .x = 0, .y = 0 };
2058 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
2059 FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
2061 /* Get the glyph bitmap and its bounding box and all the associated properties */
2062 FT_Vector pen_new = {
2063 .x = pen.x + kerning.x,
2064 .y = pen.y + kerning.y,
2066 FT_Vector pen_shadow_new = {
2067 .x = pen_new.x + p_sys->f_shadow_vector_x * (p_current_style->i_font_size << 6),
2068 .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
2070 FT_Glyph glyph;
2071 FT_BBox glyph_bbox;
2072 FT_Glyph outline;
2073 FT_BBox outline_bbox;
2074 FT_Glyph shadow;
2075 FT_BBox shadow_bbox;
2077 if( GetGlyph( p_filter,
2078 &glyph, &glyph_bbox,
2079 &outline, &outline_bbox,
2080 &shadow, &shadow_bbox,
2081 p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
2082 &pen_new, &pen_shadow_new ) )
2083 goto next;
2085 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
2086 if( outline )
2087 FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
2088 if( shadow )
2089 FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
2091 /* FIXME and what about outline */
2093 bool b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
2094 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
2095 (p_glyph_style->i_karaoke_background_alpha << 24))
2096 : (p_glyph_style->i_font_color |
2097 (p_glyph_style->i_font_alpha << 24));
2098 int i_line_offset = 0;
2099 int i_line_thickness = 0;
2100 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
2102 i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
2103 p_current_face->size->metrics.y_scale)) );
2105 i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
2106 p_current_face->size->metrics.y_scale)) );
2108 if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
2110 /* Move the baseline to make it strikethrough instead of
2111 * underline. That means that strikethrough takes precedence
2113 i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
2114 p_current_face->size->metrics.y_scale)) );
2116 else if( i_line_thickness > 0 )
2118 glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
2120 /* The real underline thickness and position are
2121 * updated once the whole line has been parsed */
2122 i_ul_offset = __MAX( i_ul_offset, i_line_offset );
2123 i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
2124 i_line_thickness = -1;
2127 FT_BBox line_bbox_new = line_bbox;
2128 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
2129 if( outline )
2130 BBoxEnlarge( &line_bbox_new, &outline_bbox );
2131 if( shadow )
2132 BBoxEnlarge( &line_bbox_new, &shadow_bbox );
2134 b_break_line = i_index > i_start &&
2135 line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
2136 if( b_break_line )
2138 FT_Done_Glyph( glyph );
2139 if( outline )
2140 FT_Done_Glyph( outline );
2141 if( shadow )
2142 FT_Done_Glyph( shadow );
2144 break_point_t *p_bp = NULL;
2145 if( break_point.i_index > i_start )
2146 p_bp = &break_point;
2147 else if( break_point_fallback.i_index > i_start )
2148 p_bp = &break_point_fallback;
2150 if( p_bp )
2152 msg_Dbg( p_filter, "Breaking line");
2153 for( int i = p_bp->i_index; i < i_index; i++ )
2155 line_character_t *ch = &p_line->p_character[i - i_start];
2156 FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
2157 if( ch->p_outline )
2158 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
2159 if( ch->p_shadow )
2160 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
2162 p_line->i_character_count = p_bp->i_index - i_start;
2164 i_index = p_bp->i_index;
2165 pen = p_bp->pen;
2166 line_bbox = p_bp->line_bbox;
2167 i_face_height = p_bp->i_face_height;
2168 i_ul_offset = p_bp->i_ul_offset;
2169 i_ul_thickness = p_bp->i_ul_thickness;
2171 else
2173 msg_Err( p_filter, "Breaking unbreakable line");
2175 break;
2178 assert( p_line->i_character_count == i_index - i_start);
2179 p_line->p_character[p_line->i_character_count++] = (line_character_t){
2180 .p_glyph = (FT_BitmapGlyph)glyph,
2181 .p_outline = (FT_BitmapGlyph)outline,
2182 .p_shadow = (FT_BitmapGlyph)shadow,
2183 .i_color = i_color,
2184 .i_line_offset = i_line_offset,
2185 .i_line_thickness = i_line_thickness,
2188 pen.x = pen_new.x + p_current_face->glyph->advance.x;
2189 pen.y = pen_new.y + p_current_face->glyph->advance.y;
2190 line_bbox = line_bbox_new;
2191 next:
2192 i_glyph_last = i_glyph_index;
2193 i_part_length--;
2194 i_index++;
2196 if( character == ' ' || character == '\t' )
2197 SAVE_BP( break_point );
2198 else if( character == 160 )
2199 SAVE_BP( break_point_fallback );
2201 if( b_break_line )
2202 break;
2204 #undef SAVE_BP
2205 /* Update our baseline */
2206 if( i_face_height_previous > 0 )
2207 i_base_line += __MAX(i_face_height, i_face_height_previous);
2208 if( i_face_height > 0 )
2209 i_face_height_previous = i_face_height;
2211 /* Update the line bbox with the actual base line */
2212 if (line_bbox.yMax > line_bbox.yMin) {
2213 line_bbox.yMin -= i_base_line;
2214 line_bbox.yMax -= i_base_line;
2216 BBoxEnlarge( &bbox, &line_bbox );
2218 /* Terminate and append the line */
2219 if( p_line )
2221 p_line->i_width = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
2222 p_line->i_base_line = i_base_line;
2223 if( i_ul_thickness > 0 )
2225 for( int i = 0; i < p_line->i_character_count; i++ )
2227 line_character_t *ch = &p_line->p_character[i];
2228 if( ch->i_line_thickness < 0 )
2230 ch->i_line_offset = i_ul_offset;
2231 ch->i_line_thickness = i_ul_thickness;
2236 *pp_line_next = p_line;
2237 pp_line_next = &p_line->p_next;
2240 *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
2242 /* Skip what we have rendered and the line delimitor if present */
2243 i_start = i_index;
2244 if( i_start < i_len && psz_text[i_start] == '\n' )
2245 i_start++;
2247 if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
2249 msg_Err( p_filter, "Truncated too high subtitle" );
2250 break;
2253 if( p_face )
2254 FT_Done_Face( p_face );
2256 free( pp_fribidi_styles );
2257 free( p_fribidi_string );
2258 free( pi_karaoke_bar );
2260 *p_bbox = bbox;
2261 return VLC_SUCCESS;
2265 * This function renders a text subpicture region into another one.
2266 * It also calculates the size needed for this string, and renders the
2267 * needed glyphs into memory. It is used as pf_add_string callback in
2268 * the vout method by this module
2270 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
2271 subpicture_region_t *p_region_in, bool b_html,
2272 const vlc_fourcc_t *p_chroma_list )
2274 filter_sys_t *p_sys = p_filter->p_sys;
2276 if( !p_region_in )
2277 return VLC_EGENERIC;
2278 if( b_html && !p_region_in->psz_html )
2279 return VLC_EGENERIC;
2280 if( !b_html && !p_region_in->psz_text )
2281 return VLC_EGENERIC;
2283 const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
2284 : p_region_in->psz_text );
2286 uint32_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
2287 text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
2288 if( !psz_text || !pp_styles )
2290 free( psz_text );
2291 free( pp_styles );
2292 return VLC_EGENERIC;
2295 /* Reset the default fontsize in case screen metrics have changed */
2296 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2298 /* */
2299 int rv = VLC_SUCCESS;
2300 int i_text_length = 0;
2301 FT_BBox bbox;
2302 int i_max_face_height;
2303 line_desc_t *p_lines = NULL;
2305 uint32_t *pi_k_durations = NULL;
2307 #ifdef HAVE_STYLES
2308 if( b_html )
2310 stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2311 (uint8_t *) p_region_in->psz_html,
2312 strlen( p_region_in->psz_html ),
2313 true );
2314 if( unlikely(p_sub == NULL) )
2315 return VLC_SUCCESS;
2317 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2318 if( !p_xml_reader )
2319 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2320 else
2321 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2322 p_filter->p_sys->p_xml = p_xml_reader;
2324 if( !p_xml_reader )
2325 rv = VLC_EGENERIC;
2327 if( !rv )
2329 /* Look for Root Node */
2330 const char *node;
2332 if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2334 if( strcasecmp( "karaoke", node ) == 0 )
2336 pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
2338 else if( strcasecmp( "text", node ) != 0 )
2340 /* Only text and karaoke tags are supported */
2341 msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2342 node );
2343 rv = VLC_EGENERIC;
2346 else
2348 msg_Err( p_filter, "Malformed HTML subtitle" );
2349 rv = VLC_EGENERIC;
2352 if( !rv )
2354 rv = ProcessNodes( p_filter,
2355 psz_text, pp_styles, pi_k_durations, &i_text_length,
2356 p_xml_reader, p_region_in->p_style );
2359 if( p_xml_reader )
2360 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2362 stream_Delete( p_sub );
2364 else
2365 #endif
2367 text_style_t *p_style;
2368 if( p_region_in->p_style )
2369 p_style = CreateStyle( p_region_in->p_style->psz_fontname,
2370 p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
2371 : p_sys->i_font_size,
2372 (p_region_in->p_style->i_font_color & 0xffffff) |
2373 ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2374 0x00ffffff,
2375 p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2376 STYLE_ITALIC |
2377 STYLE_UNDERLINE |
2378 STYLE_STRIKEOUT) );
2379 else
2380 p_style = CreateStyle( p_sys->psz_fontfamily,
2381 p_sys->i_font_size,
2382 (p_sys->i_font_color & 0xffffff) |
2383 ((p_sys->i_font_opacity & 0xff) << 24),
2384 0x00ffffff, 0);
2385 if( p_sys->b_font_bold )
2386 p_style->i_style_flags |= STYLE_BOLD;
2388 i_text_length = SetupText( p_filter,
2389 psz_text,
2390 pp_styles,
2391 NULL,
2392 p_region_in->psz_text, p_style, 0 );
2395 if( !rv && i_text_length > 0 )
2397 rv = ProcessLines( p_filter,
2398 &p_lines, &bbox, &i_max_face_height,
2399 psz_text, pp_styles, pi_k_durations, i_text_length );
2402 p_region_out->i_x = p_region_in->i_x;
2403 p_region_out->i_y = p_region_in->i_y;
2405 /* Don't attempt to render text that couldn't be layed out
2406 * properly. */
2407 if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
2409 const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
2410 const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
2412 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2413 p_chroma_list = p_chroma_list_yuvp;
2414 else if( !p_chroma_list || *p_chroma_list == 0 )
2415 p_chroma_list = p_chroma_list_rgba;
2417 const int i_margin = p_sys->i_background_opacity > 0 ? i_max_face_height / 4 : 0;
2418 for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
2420 rv = VLC_EGENERIC;
2421 if( *p_chroma == VLC_CODEC_YUVP )
2422 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
2423 else if( *p_chroma == VLC_CODEC_YUVA )
2424 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2425 VLC_CODEC_YUVA,
2426 YUVFromRGB,
2427 FillYUVAPicture,
2428 BlendYUVAPixel );
2429 else if( *p_chroma == VLC_CODEC_RGBA )
2430 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2431 VLC_CODEC_RGBA,
2432 RGBFromRGB,
2433 FillRGBAPicture,
2434 BlendRGBAPixel );
2435 if( !rv )
2436 break;
2439 /* With karaoke, we're going to have to render the text a number
2440 * of times to show the progress marker on the text.
2442 if( pi_k_durations )
2443 var_SetBool( p_filter, "text-rerender", true );
2446 FreeLines( p_lines );
2448 free( psz_text );
2449 for( int i = 0; i < i_text_length; i++ )
2451 if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2452 text_style_Delete( pp_styles[i] );
2454 free( pp_styles );
2455 free( pi_k_durations );
2457 return rv;
2460 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2461 subpicture_region_t *p_region_in,
2462 const vlc_fourcc_t *p_chroma_list )
2464 return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
2467 #ifdef HAVE_STYLES
2469 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2470 subpicture_region_t *p_region_in,
2471 const vlc_fourcc_t *p_chroma_list )
2473 return RenderCommon( p_filter, p_region_out, p_region_in, true, p_chroma_list );
2476 #endif
2478 /*****************************************************************************
2479 * Create: allocates osd-text video thread output method
2480 *****************************************************************************
2481 * This function allocates and initializes a Clone vout method.
2482 *****************************************************************************/
2483 static int Create( vlc_object_t *p_this )
2485 filter_t *p_filter = (filter_t *)p_this;
2486 filter_sys_t *p_sys;
2487 char *psz_fontfile = NULL;
2488 char *psz_fontfamily = NULL;
2489 int i_error = 0, fontindex = 0;
2491 /* Allocate structure */
2492 p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2493 if( !p_sys )
2494 return VLC_ENOMEM;
2496 p_sys->psz_fontfamily = NULL;
2497 #ifdef HAVE_STYLES
2498 p_sys->p_xml = NULL;
2499 #endif
2500 p_sys->p_face = 0;
2501 p_sys->p_library = 0;
2502 p_sys->i_font_size = 0;
2503 p_sys->i_display_height = 0;
2505 var_Create( p_filter, "freetype-rel-fontsize",
2506 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
2508 psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
2509 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2510 p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
2511 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
2512 p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2513 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
2514 p_sys->b_font_bold = var_InheritBool( p_filter, "freetype-bold" );
2516 p_sys->i_background_opacity = var_InheritInteger( p_filter,"freetype-background-opacity" );;
2517 p_sys->i_background_opacity = __MAX( __MIN( p_sys->i_background_opacity, 255 ), 0 );
2518 p_sys->i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
2519 p_sys->i_background_color = __MAX( __MIN( p_sys->i_background_color, 0xFFFFFF ), 0 );
2521 p_sys->f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
2522 p_sys->f_outline_thickness = __MAX( __MIN( p_sys->f_outline_thickness, 0.5 ), 0.0 );
2523 p_sys->i_outline_opacity = var_InheritInteger( p_filter, "freetype-outline-opacity" );
2524 p_sys->i_outline_opacity = __MAX( __MIN( p_sys->i_outline_opacity, 255 ), 0 );
2525 p_sys->i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
2526 p_sys->i_outline_color = __MAX( __MIN( p_sys->i_outline_color, 0xFFFFFF ), 0 );
2528 p_sys->i_shadow_opacity = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
2529 p_sys->i_shadow_opacity = __MAX( __MIN( p_sys->i_shadow_opacity, 255 ), 0 );
2530 p_sys->i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
2531 p_sys->i_shadow_color = __MAX( __MIN( p_sys->i_shadow_color, 0xFFFFFF ), 0 );
2532 float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
2533 float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
2534 f_shadow_distance = __MAX( __MIN( f_shadow_distance, 1 ), 0 );
2535 p_sys->f_shadow_vector_x = f_shadow_distance * cos(2 * M_PI * f_shadow_angle / 360);
2536 p_sys->f_shadow_vector_y = f_shadow_distance * sin(2 * M_PI * f_shadow_angle / 360);
2538 #ifdef WIN32
2539 /* Get Windows Font folder */
2540 wchar_t wdir[MAX_PATH];
2541 if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2543 GetWindowsDirectoryW( wdir, MAX_PATH );
2544 wcscat( wdir, L"\\fonts" );
2546 p_sys->psz_win_fonts_path = FromWide( wdir );
2547 #endif
2549 /* Set default psz_fontfamily */
2550 if( !psz_fontfamily || !*psz_fontfamily )
2552 free( psz_fontfamily );
2553 #ifdef HAVE_STYLES
2554 psz_fontfamily = strdup( DEFAULT_FAMILY );
2555 #else
2556 # ifdef WIN32
2557 if( asprintf( &psz_fontfamily, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2558 goto error;
2559 # else
2560 psz_fontfamily = strdup( DEFAULT_FONT_FILE );
2561 # endif
2562 msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
2563 #endif
2566 /* Set the current font file */
2567 p_sys->psz_fontfamily = psz_fontfamily;
2568 #ifdef HAVE_STYLES
2569 #ifdef HAVE_FONTCONFIG
2570 FontConfig_BuildCache( p_filter );
2572 /* */
2573 psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
2574 p_sys->i_default_font_size, &fontindex );
2575 #elif defined(WIN32)
2576 psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
2577 p_sys->i_default_font_size, &fontindex );
2579 #endif
2580 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
2582 /* If nothing is found, use the default family */
2583 if( !psz_fontfile )
2584 psz_fontfile = strdup( psz_fontfamily );
2586 #else /* !HAVE_STYLES */
2587 /* Use the default file */
2588 psz_fontfile = psz_fontfamily;
2589 #endif
2591 /* */
2592 i_error = FT_Init_FreeType( &p_sys->p_library );
2593 if( i_error )
2595 msg_Err( p_filter, "couldn't initialize freetype" );
2596 goto error;
2599 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2600 fontindex, &p_sys->p_face );
2602 if( i_error == FT_Err_Unknown_File_Format )
2604 msg_Err( p_filter, "file %s have unknown format",
2605 psz_fontfile ? psz_fontfile : "(null)" );
2606 goto error;
2608 else if( i_error )
2610 msg_Err( p_filter, "failed to load font file %s",
2611 psz_fontfile ? psz_fontfile : "(null)" );
2612 goto error;
2615 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2616 if( i_error )
2618 msg_Err( p_filter, "font has no unicode translation table" );
2619 goto error;
2622 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2624 p_sys->p_stroker = NULL;
2625 if( p_sys->f_outline_thickness > 0.001 )
2627 i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
2628 if( i_error )
2629 msg_Err( p_filter, "Failed to create stroker for outlining" );
2632 p_sys->pp_font_attachments = NULL;
2633 p_sys->i_font_attachments = 0;
2635 p_filter->pf_render_text = RenderText;
2636 #ifdef HAVE_STYLES
2637 p_filter->pf_render_html = RenderHtml;
2638 #else
2639 p_filter->pf_render_html = NULL;
2640 #endif
2642 LoadFontsFromAttachments( p_filter );
2644 #ifdef HAVE_STYLES
2645 free( psz_fontfile );
2646 #endif
2648 return VLC_SUCCESS;
2650 error:
2651 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2652 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2653 #ifdef HAVE_STYLES
2654 free( psz_fontfile );
2655 #endif
2656 free( psz_fontfamily );
2657 free( p_sys );
2658 return VLC_EGENERIC;
2661 /*****************************************************************************
2662 * Destroy: destroy Clone video thread output method
2663 *****************************************************************************
2664 * Clean up all data and library connections
2665 *****************************************************************************/
2666 static void Destroy( vlc_object_t *p_this )
2668 filter_t *p_filter = (filter_t *)p_this;
2669 filter_sys_t *p_sys = p_filter->p_sys;
2671 if( p_sys->pp_font_attachments )
2673 for( int k = 0; k < p_sys->i_font_attachments; k++ )
2674 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2676 free( p_sys->pp_font_attachments );
2679 #ifdef HAVE_STYLES
2680 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2681 #endif
2682 free( p_sys->psz_fontfamily );
2684 /* FcFini asserts calling the subfunction FcCacheFini()
2685 * even if no other library functions have been made since FcInit(),
2686 * so don't call it. */
2688 if( p_sys->p_stroker )
2689 FT_Stroker_Done( p_sys->p_stroker );
2690 FT_Done_Face( p_sys->p_face );
2691 FT_Done_FreeType( p_sys->p_library );
2692 free( p_sys );