1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2011 the VideoLAN team
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 /*****************************************************************************
29 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_stream.h> /* stream_MemoryNew */
38 #include <vlc_input.h> /* vlc_input_attachment_* */
39 #include <vlc_xml.h> /* xml_reader */
40 #include <vlc_strings.h> /* resolve_xml_special_chars */
41 #include <vlc_charset.h> /* ToCharset */
42 #include <vlc_dialog.h> /* FcCache dialog */
43 #include <vlc_filter.h> /* filter_sys_t */
44 #include <vlc_text_style.h> /* text_style_t*/
48 # define DEFAULT_FONT_FILE "/Library/Fonts/Arial Black.ttf"
49 # define DEFAULT_FAMILY "Arial Black"
50 #elif defined( WIN32 )
51 # define DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
52 # define DEFAULT_FAMILY "Arial"
53 #elif defined( HAVE_MAEMO )
54 # define DEFAULT_FONT_FILE "/usr/share/fonts/nokia/nosnb.ttf"
55 # define DEFAULT_FAMILY "Nokia Sans Bold"
57 # define DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
58 # define DEFAULT_FAMILY "Serif Bold"
62 #include <freetype/ftsynth.h>
63 #include FT_FREETYPE_H
65 #define FT_FLOOR(X) ((X & -64) >> 6)
66 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
68 #define FT_MulFix(v, s) (((v)*(s))>>16)
72 #if defined(HAVE_FRIBIDI)
73 # include <fribidi/fribidi.h>
81 # undef HAVE_FONTCONFIG
85 #ifdef HAVE_FONTCONFIG
86 # include <fontconfig/fontconfig.h>
92 /*****************************************************************************
94 *****************************************************************************/
95 static int Create ( vlc_object_t * );
96 static void Destroy( vlc_object_t * );
98 #define FONT_TEXT N_("Font")
100 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
101 #define FONT_LONGTEXT N_("Font file for the font you want to use")
103 #define FONTSIZE_TEXT N_("Font size in pixels")
104 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
105 "that will be rendered on the video. " \
106 "If set to something different than 0 this option will override the " \
107 "relative font size." )
108 #define OPACITY_TEXT N_("Opacity")
109 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
110 "text that will be rendered on the video. 0 = transparent, " \
111 "255 = totally opaque. " )
112 #define COLOR_TEXT N_("Text default color")
113 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
114 "the video. This must be an hexadecimal (like HTML colors). The first two "\
115 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
116 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
117 #define FONTSIZER_TEXT N_("Relative font size")
118 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
119 "fonts that will be rendered on the video. If absolute font size is set, "\
120 "relative size will be overridden." )
122 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
123 static const char *const ppsz_sizes_text[] = {
124 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
125 #define YUVP_TEXT N_("Use YUVP renderer")
126 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
127 "This option is only needed if you want to encode into DVB subtitles" )
128 #define EFFECT_TEXT N_("Font Effect")
129 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
130 "text to improve its readability." )
132 enum { EFFECT_BACKGROUND = 1,
134 EFFECT_OUTLINE_FAT = 3,
136 static int const pi_effects[] = { EFFECT_BACKGROUND, EFFECT_OUTLINE, EFFECT_OUTLINE_FAT };
137 static const char *const ppsz_effects_text[] = {
138 N_("Background"),N_("Outline"), N_("Fat Outline") };
140 static const int pi_color_values[] = {
141 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
142 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
143 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
145 static const char *const ppsz_color_descriptions[] = {
146 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
147 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
148 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
151 set_shortname( N_("Text renderer"))
152 set_description( N_("Freetype2 font renderer") )
153 set_category( CAT_VIDEO )
154 set_subcategory( SUBCAT_VIDEO_SUBPIC )
157 add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
159 add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
162 add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
163 FONTSIZE_LONGTEXT, true )
166 /* opacity valid on 0..255, with default 255 = fully opaque */
167 add_integer_with_range( "freetype-opacity", 255, 0, 255,
168 OPACITY_TEXT, OPACITY_LONGTEXT, false )
171 /* hook to the color values list, with default 0x00ffffff = white */
172 add_integer( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
173 COLOR_LONGTEXT, false )
174 change_integer_list( pi_color_values, ppsz_color_descriptions )
177 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
178 FONTSIZER_LONGTEXT, false )
179 change_integer_list( pi_sizes, ppsz_sizes_text )
182 add_integer( "freetype-effect", 2, EFFECT_TEXT,
183 EFFECT_LONGTEXT, false )
184 change_integer_list( pi_effects, ppsz_effects_text )
187 add_bool( "freetype-yuvp", false, YUVP_TEXT,
188 YUVP_LONGTEXT, true )
189 set_capability( "text renderer", 100 )
190 add_shortcut( "text" )
191 set_callbacks( Create, Destroy )
195 /*****************************************************************************
197 *****************************************************************************/
199 typedef struct line_desc_t line_desc_t;
202 /** NULL-terminated list of glyphs making the string */
203 FT_BitmapGlyph *pp_glyphs;
204 /** list of relative positions for the glyphs */
205 FT_Vector *p_glyph_pos;
206 /** list of RGB information for styled text */
209 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
210 /** underline information -- only supplied if text should be underlined */
211 int *pi_underline_offset;
212 uint16_t *pi_underline_thickness;
222 typedef struct font_stack_t font_stack_t;
227 uint32_t i_color; /* ARGB */
228 uint32_t i_karaoke_bg_color; /* ARGB */
230 font_stack_t *p_next;
233 /*****************************************************************************
234 * filter_sys_t: freetype local data
235 *****************************************************************************
236 * This structure is part of the video output thread descriptor.
237 * It describes the freetype specific properties of an output thread.
238 *****************************************************************************/
241 FT_Library p_library; /* handle to library */
242 FT_Face p_face; /* handle to face object */
243 uint8_t i_font_opacity;
248 int i_default_font_size;
249 int i_display_height;
250 char* psz_fontfamily;
254 char* psz_win_fonts_path;
258 input_attachment_t **pp_font_attachments;
259 int i_font_attachments;
263 static void YUVFromRGB( uint32_t i_argb,
264 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
266 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
267 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
268 int i_blue = ( i_argb & 0x000000ff );
270 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
271 802 * i_blue + 4096 + 131072 ) >> 13, 235);
272 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
273 3598 * i_blue + 4096 + 1048576) >> 13, 240);
274 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
275 -585 * i_blue + 4096 + 1048576) >> 13, 240);
278 /*****************************************************************************
279 * Make any TTF/OTF fonts present in the attachments of the media file
280 * and store them for later use by the FreeType Engine
281 *****************************************************************************/
282 static int LoadFontsFromAttachments( filter_t *p_filter )
284 filter_sys_t *p_sys = p_filter->p_sys;
285 input_attachment_t **pp_attachments;
286 int i_attachments_cnt;
288 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
291 p_sys->i_font_attachments = 0;
292 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
293 if( !p_sys->pp_font_attachments )
296 for( int k = 0; k < i_attachments_cnt; k++ )
298 input_attachment_t *p_attach = pp_attachments[k];
300 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
301 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
302 p_attach->i_data > 0 && p_attach->p_data )
304 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
308 vlc_input_attachment_Delete( p_attach );
311 free( pp_attachments );
316 static int GetFontSize( filter_t *p_filter )
318 filter_sys_t *p_sys = p_filter->p_sys;
321 if( p_sys->i_default_font_size )
323 i_size = p_sys->i_default_font_size;
327 int i_ratio = var_GetInteger( p_filter, "freetype-rel-fontsize" );
330 i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
331 p_filter->p_sys->i_display_height = p_filter->fmt_out.video.i_height;
336 msg_Warn( p_filter, "invalid fontsize, using 12" );
342 static int SetFontSize( filter_t *p_filter, int i_size )
344 filter_sys_t *p_sys = p_filter->p_sys;
348 i_size = GetFontSize( p_filter );
350 msg_Dbg( p_filter, "using fontsize: %i", i_size );
353 p_sys->i_font_size = i_size;
355 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
357 msg_Err( p_filter, "couldn't set font size to %d", i_size );
365 #ifdef HAVE_FONTCONFIG
366 static void FontConfig_BuildCache( filter_t *p_filter )
369 msg_Dbg( p_filter, "Building font databases.");
374 dialog_progress_bar_t *p_dialog = NULL;
375 FcConfig *fcConfig = FcInitLoadConfig();
377 p_dialog = dialog_ProgressCreate( p_filter,
378 _("Building font cache"),
379 _("Please wait while your font cache is rebuilt.\n"
380 "This should take less than a few minutes."), NULL );
383 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
385 FcConfigBuildFonts( fcConfig );
388 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
389 dialog_ProgressDestroy( p_dialog );
394 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
398 * \brief Selects a font matching family, bold, italic provided
400 static char* FontConfig_Select( FcConfig* config, const char* family,
401 bool b_bold, bool b_italic, int i_size, int *i_idx )
403 FcResult result = FcResultMatch;
404 FcPattern *pat, *p_pat;
408 /* Create a pattern and fills it */
409 pat = FcPatternCreate();
410 if (!pat) return NULL;
413 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
414 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
415 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
416 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
420 if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
422 FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
423 free( psz_fontsize );
428 FcDefaultSubstitute( pat );
429 if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
431 FcPatternDestroy( pat );
435 /* Find the best font for the pattern, destroy the pattern */
436 p_pat = FcFontMatch( config, pat, &result );
437 FcPatternDestroy( pat );
438 if( !p_pat || result == FcResultNoMatch ) return NULL;
440 /* Check the new pattern */
441 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
442 || ( val_b != FcTrue ) )
444 FcPatternDestroy( p_pat );
447 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
452 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
454 FcPatternDestroy( p_pat );
458 /* if( strcasecmp((const char*)val_s, family ) != 0 )
459 msg_Warn( p_filter, "fontconfig: selected font family is not"
460 "the requested one: '%s' != '%s'\n",
461 (const char*)val_s, family ); */
463 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
465 FcPatternDestroy( p_pat );
469 FcPatternDestroy( p_pat );
470 return strdup( (const char*)val_s );
476 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
478 static int GetFileFontByName( const char *font_name, char **psz_filename )
481 wchar_t vbuffer[MAX_PATH];
482 wchar_t dbuffer[256];
484 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey) != ERROR_SUCCESS )
487 for( int index = 0;; index++ )
489 DWORD vbuflen = MAX_PATH - 1;
492 if( RegEnumValueW( hKey, index, vbuffer, &vbuflen,
493 NULL, NULL, (LPBYTE)dbuffer, &dbuflen) != ERROR_SUCCESS )
496 char *psz_value = FromWide( vbuffer );
498 char *s = strchr( psz_value,'(' );
499 if( s != NULL && s != psz_value ) s[-1] = '\0';
501 /* Manage concatenated font names */
502 if( strchr( psz_value, '&') ) {
503 if( strcasestr( psz_value, font_name ) != NULL )
507 if( strcasecmp( psz_value, font_name ) == 0 )
512 *psz_filename = FromWide( dbuffer );
517 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
518 DWORD type, LPARAM lParam)
520 VLC_UNUSED( metric );
521 if( (type & RASTER_FONTTYPE) ) return 1;
522 // if( lpelfe->elfScript ) FIXME
524 return GetFileFontByName( (const char *)lpelfe->elfFullName, (char **)lParam );
527 static char* Win32_Select( filter_t *p_filter, const char* family,
528 bool b_bold, bool b_italic, int i_size, int *i_idx )
530 VLC_UNUSED( i_size );
531 // msg_Dbg( p_filter, "Here in Win32_Select, asking for %s", family );
535 lf.lfCharSet = DEFAULT_CHARSET;
539 lf.lfWeight = FW_BOLD;
540 strncpy( (LPSTR)&lf.lfFaceName, family, 32);
543 char *psz_filename = NULL;
544 HDC hDC = GetDC( NULL );
545 EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
546 ReleaseDC(NULL, hDC);
548 if( psz_filename == NULL )
551 /* FIXME: increase i_idx, when concatenated strings */
556 if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
565 /*****************************************************************************
566 * RenderYUVP: place string in picture
567 *****************************************************************************
568 * This function merges the previously rendered freetype glyphs into a picture
569 *****************************************************************************/
570 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
571 line_desc_t *p_line, int i_width, int i_height )
573 VLC_UNUSED(p_filter);
574 static const uint8_t pi_gamma[16] =
575 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
576 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
580 int i, x, y, i_pitch;
581 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
583 /* Create a new subpicture region */
584 memset( &fmt, 0, sizeof(video_format_t) );
585 fmt.i_chroma = VLC_CODEC_YUVP;
586 fmt.i_width = fmt.i_visible_width = i_width + 4;
587 fmt.i_height = fmt.i_visible_height = i_height + 4;
588 if( p_region->fmt.i_visible_width > 0 )
589 fmt.i_visible_width = p_region->fmt.i_visible_width;
590 if( p_region->fmt.i_visible_height > 0 )
591 fmt.i_visible_height = p_region->fmt.i_visible_height;
592 fmt.i_x_offset = fmt.i_y_offset = 0;
596 assert( !p_region->p_picture );
597 p_region->p_picture = picture_NewFromFormat( &fmt );
598 if( !p_region->p_picture )
600 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
603 /* Calculate text color components
604 * Only use the first color */
605 YUVFromRGB( p_line->p_fg_rgb[ 0 ], &i_y, &i_u, &i_v );
608 fmt.p_palette->i_entries = 16;
609 for( i = 0; i < 8; i++ )
611 fmt.p_palette->palette[i][0] = 0;
612 fmt.p_palette->palette[i][1] = 0x80;
613 fmt.p_palette->palette[i][2] = 0x80;
614 fmt.p_palette->palette[i][3] = pi_gamma[i];
615 fmt.p_palette->palette[i][3] =
616 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
618 for( i = 8; i < fmt.p_palette->i_entries; i++ )
620 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
621 fmt.p_palette->palette[i][1] = i_u;
622 fmt.p_palette->palette[i][2] = i_v;
623 fmt.p_palette->palette[i][3] = pi_gamma[i];
624 fmt.p_palette->palette[i][3] =
625 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
628 p_dst = p_region->p_picture->Y_PIXELS;
629 i_pitch = p_region->p_picture->Y_PITCH;
631 /* Initialize the region pixels */
632 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
634 for( ; p_line != NULL; p_line = p_line->p_next )
636 int i_glyph_tmax = 0;
637 int i_bitmap_offset, i_offset, i_align_offset = 0;
638 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
640 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
641 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
644 if( p_line->i_width < i_width )
646 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
648 i_align_offset = i_width - p_line->i_width;
650 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
652 i_align_offset = ( i_width - p_line->i_width ) / 2;
656 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
658 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
660 i_offset = ( p_line->p_glyph_pos[ i ].y +
661 i_glyph_tmax - p_glyph->top + 2 ) *
662 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
665 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
667 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
669 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
671 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
678 /* Outlining (find something better than nearest neighbour filtering ?) */
681 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
682 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
683 uint8_t left, current;
685 for( y = 1; y < (int)fmt.i_height - 1; y++ )
687 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
688 p_dst += p_region->p_picture->Y_PITCH;
691 for( x = 1; x < (int)fmt.i_width - 1; x++ )
694 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
695 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;
699 memset( p_top, 0, fmt.i_width );
705 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
706 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
707 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
708 int i_glyph_tmax, int i_align_offset,
709 uint8_t i_y, uint8_t i_u, uint8_t i_v,
710 subpicture_region_t *p_region)
713 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
715 p_dst_y = p_region->p_picture->Y_PIXELS;
716 p_dst_u = p_region->p_picture->U_PIXELS;
717 p_dst_v = p_region->p_picture->V_PIXELS;
718 p_dst_a = p_region->p_picture->A_PIXELS;
719 i_pitch = p_region->p_picture->A_PITCH;
721 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
722 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
724 for( int y = 0; y < i_line_thickness; y++ )
726 int i_extra = p_this_glyph->bitmap.width;
730 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
731 (p_this_glyph_pos->x + p_this_glyph->left);
733 for( int x = 0; x < i_extra; x++ )
737 /* break the underline around the tails of any glyphs which cross it */
738 /* Strikethrough doesn't get broken */
739 for( int z = x - i_line_thickness;
740 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
743 if( p_next_glyph && ( z >= i_extra ) )
745 int i_row = i_line_offset + p_next_glyph->top + y;
747 if( ( p_next_glyph->bitmap.rows > i_row ) &&
748 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
753 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
755 int i_row = i_line_offset + p_this_glyph->top + y;
757 if( ( p_this_glyph->bitmap.rows > i_row ) &&
758 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
767 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
768 p_dst_u[i_offset+x] = i_u;
769 p_dst_v[i_offset+x] = i_v;
770 p_dst_a[i_offset+x] = 255;
777 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
779 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
780 int i_pitch = p_region->p_picture->A_PITCH;
783 for( ; p_line != NULL; p_line = p_line->p_next )
785 int i_glyph_tmax=0, i = 0;
786 int i_bitmap_offset, i_offset, i_align_offset = 0;
787 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
789 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
790 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
793 if( p_line->i_width < i_width )
795 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
797 i_align_offset = i_width - p_line->i_width;
799 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
801 i_align_offset = ( i_width - p_line->i_width ) / 2;
805 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
807 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
809 i_offset = ( p_line->p_glyph_pos[ i ].y +
810 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
811 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
812 i_align_offset +xoffset;
814 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
816 for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
818 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
819 if( p_dst[i_offset+x] <
820 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
822 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
830 /*****************************************************************************
831 * RenderYUVA: place string in picture
832 *****************************************************************************
833 * This function merges the previously rendered freetype glyphs into a picture
834 *****************************************************************************/
835 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
836 line_desc_t *p_line, int i_width, int i_height )
838 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
840 int i, y, i_pitch, i_alpha;
842 if( i_width == 0 || i_height == 0 )
845 /* Create a new subpicture region */
846 memset( &fmt, 0, sizeof(video_format_t) );
847 fmt.i_chroma = VLC_CODEC_YUVA;
848 fmt.i_width = fmt.i_visible_width = i_width + 6;
849 fmt.i_height = fmt.i_visible_height = i_height + 6;
850 if( p_region->fmt.i_visible_width > 0 )
851 fmt.i_visible_width = p_region->fmt.i_visible_width;
852 if( p_region->fmt.i_visible_height > 0 )
853 fmt.i_visible_height = p_region->fmt.i_visible_height;
854 fmt.i_x_offset = fmt.i_y_offset = 0;
858 p_region->p_picture = picture_NewFromFormat( &fmt );
859 if( !p_region->p_picture )
863 /* Save the alpha value */
864 i_alpha = p_line->i_alpha;
866 p_dst_y = p_region->p_picture->Y_PIXELS;
867 p_dst_u = p_region->p_picture->U_PIXELS;
868 p_dst_v = p_region->p_picture->V_PIXELS;
869 p_dst_a = p_region->p_picture->A_PIXELS;
870 i_pitch = p_region->p_picture->A_PITCH;
872 /* Initialize the region pixels */
873 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
874 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
875 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
877 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
878 memset( p_dst_a, 0x00, i_pitch * p_region->fmt.i_height );
880 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
882 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
883 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
885 DrawBlack( p_line, i_width, p_region, 0, 0);
886 DrawBlack( p_line, i_width, p_region, -1, 0);
887 DrawBlack( p_line, i_width, p_region, 0, -1);
888 DrawBlack( p_line, i_width, p_region, 1, 0);
889 DrawBlack( p_line, i_width, p_region, 0, 1);
892 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
894 DrawBlack( p_line, i_width, p_region, -1, -1);
895 DrawBlack( p_line, i_width, p_region, -1, 1);
896 DrawBlack( p_line, i_width, p_region, 1, -1);
897 DrawBlack( p_line, i_width, p_region, 1, 1);
899 DrawBlack( p_line, i_width, p_region, -2, 0);
900 DrawBlack( p_line, i_width, p_region, 0, -2);
901 DrawBlack( p_line, i_width, p_region, 2, 0);
902 DrawBlack( p_line, i_width, p_region, 0, 2);
904 DrawBlack( p_line, i_width, p_region, -2, -2);
905 DrawBlack( p_line, i_width, p_region, -2, 2);
906 DrawBlack( p_line, i_width, p_region, 2, -2);
907 DrawBlack( p_line, i_width, p_region, 2, 2);
909 DrawBlack( p_line, i_width, p_region, -3, 0);
910 DrawBlack( p_line, i_width, p_region, 0, -3);
911 DrawBlack( p_line, i_width, p_region, 3, 0);
912 DrawBlack( p_line, i_width, p_region, 0, 3);
915 for( ; p_line != NULL; p_line = p_line->p_next )
917 int i_glyph_tmax = 0;
918 int i_bitmap_offset, i_offset, i_align_offset = 0;
919 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
921 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
922 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
925 if( p_line->i_width < i_width )
927 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
929 i_align_offset = i_width - p_line->i_width;
931 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
933 i_align_offset = ( i_width - p_line->i_width ) / 2;
937 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
939 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
941 i_offset = ( p_line->p_glyph_pos[ i ].y +
942 i_glyph_tmax - p_glyph->top + 3 ) *
943 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
946 /* Every glyph can (and in fact must) have its own color */
947 uint8_t i_y, i_u, i_v;
948 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
950 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
952 for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
954 uint8_t i_y_local = i_y;
955 uint8_t i_u_local = i_u;
956 uint8_t i_v_local = i_v;
958 if( p_line->p_fg_bg_ratio != 0x00 )
960 int i_split = p_glyph->bitmap.width *
961 p_line->p_fg_bg_ratio[ i ] / 0x7f;
965 YUVFromRGB( p_line->p_bg_rgb[ i ],
966 &i_y_local, &i_u_local, &i_v_local );
970 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
972 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
973 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
975 p_dst_u[i_offset+x] = i_u;
976 p_dst_v[i_offset+x] = i_v;
978 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
979 p_dst_a[i_offset+x] = 0xff;
985 if( p_line->pi_underline_thickness[ i ] )
987 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
988 p_line->pi_underline_offset[ i ],
989 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
990 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
991 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
992 i_glyph_tmax, i_align_offset,
999 /* Apply the alpha setting */
1000 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1001 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1006 static text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1007 uint32_t i_font_color, uint32_t i_karaoke_bg_color,
1010 text_style_t *p_style = text_style_New();
1014 p_style->psz_fontname = strdup( psz_fontname );
1015 p_style->i_font_size = i_font_size;
1016 p_style->i_font_color = (i_font_color & 0x00ffffff) >> 0;
1017 p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
1018 p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >> 0;
1019 p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
1020 p_style->i_style_flags |= i_style_flags;
1024 static bool StyleEquals( text_style_t *s1, text_style_t *s2 )
1031 return s1->i_font_size == s2->i_font_size &&
1032 s1->i_font_color == s2->i_font_color &&
1033 s1->i_font_alpha == s2->i_font_alpha &&
1034 s1->i_style_flags == s2->i_style_flags &&
1035 !strcmp( s1->psz_fontname, s2->psz_fontname );
1038 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1039 uint32_t i_color, uint32_t i_karaoke_bg_color )
1042 return VLC_EGENERIC;
1044 font_stack_t *p_new = malloc( sizeof(*p_new) );
1048 p_new->p_next = NULL;
1051 p_new->psz_name = strdup( psz_name );
1053 p_new->psz_name = NULL;
1055 p_new->i_size = i_size;
1056 p_new->i_color = i_color;
1057 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1065 font_stack_t *p_last;
1067 for( p_last = *p_font;
1069 p_last = p_last->p_next )
1072 p_last->p_next = p_new;
1077 static int PopFont( font_stack_t **p_font )
1079 font_stack_t *p_last, *p_next_to_last;
1081 if( !p_font || !*p_font )
1082 return VLC_EGENERIC;
1084 p_next_to_last = NULL;
1085 for( p_last = *p_font;
1087 p_last = p_last->p_next )
1089 p_next_to_last = p_last;
1092 if( p_next_to_last )
1093 p_next_to_last->p_next = NULL;
1097 free( p_last->psz_name );
1103 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1104 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1106 font_stack_t *p_last;
1108 if( !p_font || !*p_font )
1109 return VLC_EGENERIC;
1111 for( p_last=*p_font;
1113 p_last=p_last->p_next )
1116 *psz_name = p_last->psz_name;
1117 *i_size = p_last->i_size;
1118 *i_color = p_last->i_color;
1119 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1124 static const struct {
1125 const char *psz_name;
1127 } p_html_colors[] = {
1128 /* Official html colors */
1129 { "Aqua", 0x00FFFF },
1130 { "Black", 0x000000 },
1131 { "Blue", 0x0000FF },
1132 { "Fuchsia", 0xFF00FF },
1133 { "Gray", 0x808080 },
1134 { "Green", 0x008000 },
1135 { "Lime", 0x00FF00 },
1136 { "Maroon", 0x800000 },
1137 { "Navy", 0x000080 },
1138 { "Olive", 0x808000 },
1139 { "Purple", 0x800080 },
1140 { "Red", 0xFF0000 },
1141 { "Silver", 0xC0C0C0 },
1142 { "Teal", 0x008080 },
1143 { "White", 0xFFFFFF },
1144 { "Yellow", 0xFFFF00 },
1147 { "AliceBlue", 0xF0F8FF },
1148 { "AntiqueWhite", 0xFAEBD7 },
1149 { "Aqua", 0x00FFFF },
1150 { "Aquamarine", 0x7FFFD4 },
1151 { "Azure", 0xF0FFFF },
1152 { "Beige", 0xF5F5DC },
1153 { "Bisque", 0xFFE4C4 },
1154 { "Black", 0x000000 },
1155 { "BlanchedAlmond", 0xFFEBCD },
1156 { "Blue", 0x0000FF },
1157 { "BlueViolet", 0x8A2BE2 },
1158 { "Brown", 0xA52A2A },
1159 { "BurlyWood", 0xDEB887 },
1160 { "CadetBlue", 0x5F9EA0 },
1161 { "Chartreuse", 0x7FFF00 },
1162 { "Chocolate", 0xD2691E },
1163 { "Coral", 0xFF7F50 },
1164 { "CornflowerBlue", 0x6495ED },
1165 { "Cornsilk", 0xFFF8DC },
1166 { "Crimson", 0xDC143C },
1167 { "Cyan", 0x00FFFF },
1168 { "DarkBlue", 0x00008B },
1169 { "DarkCyan", 0x008B8B },
1170 { "DarkGoldenRod", 0xB8860B },
1171 { "DarkGray", 0xA9A9A9 },
1172 { "DarkGrey", 0xA9A9A9 },
1173 { "DarkGreen", 0x006400 },
1174 { "DarkKhaki", 0xBDB76B },
1175 { "DarkMagenta", 0x8B008B },
1176 { "DarkOliveGreen", 0x556B2F },
1177 { "Darkorange", 0xFF8C00 },
1178 { "DarkOrchid", 0x9932CC },
1179 { "DarkRed", 0x8B0000 },
1180 { "DarkSalmon", 0xE9967A },
1181 { "DarkSeaGreen", 0x8FBC8F },
1182 { "DarkSlateBlue", 0x483D8B },
1183 { "DarkSlateGray", 0x2F4F4F },
1184 { "DarkSlateGrey", 0x2F4F4F },
1185 { "DarkTurquoise", 0x00CED1 },
1186 { "DarkViolet", 0x9400D3 },
1187 { "DeepPink", 0xFF1493 },
1188 { "DeepSkyBlue", 0x00BFFF },
1189 { "DimGray", 0x696969 },
1190 { "DimGrey", 0x696969 },
1191 { "DodgerBlue", 0x1E90FF },
1192 { "FireBrick", 0xB22222 },
1193 { "FloralWhite", 0xFFFAF0 },
1194 { "ForestGreen", 0x228B22 },
1195 { "Fuchsia", 0xFF00FF },
1196 { "Gainsboro", 0xDCDCDC },
1197 { "GhostWhite", 0xF8F8FF },
1198 { "Gold", 0xFFD700 },
1199 { "GoldenRod", 0xDAA520 },
1200 { "Gray", 0x808080 },
1201 { "Grey", 0x808080 },
1202 { "Green", 0x008000 },
1203 { "GreenYellow", 0xADFF2F },
1204 { "HoneyDew", 0xF0FFF0 },
1205 { "HotPink", 0xFF69B4 },
1206 { "IndianRed", 0xCD5C5C },
1207 { "Indigo", 0x4B0082 },
1208 { "Ivory", 0xFFFFF0 },
1209 { "Khaki", 0xF0E68C },
1210 { "Lavender", 0xE6E6FA },
1211 { "LavenderBlush", 0xFFF0F5 },
1212 { "LawnGreen", 0x7CFC00 },
1213 { "LemonChiffon", 0xFFFACD },
1214 { "LightBlue", 0xADD8E6 },
1215 { "LightCoral", 0xF08080 },
1216 { "LightCyan", 0xE0FFFF },
1217 { "LightGoldenRodYellow", 0xFAFAD2 },
1218 { "LightGray", 0xD3D3D3 },
1219 { "LightGrey", 0xD3D3D3 },
1220 { "LightGreen", 0x90EE90 },
1221 { "LightPink", 0xFFB6C1 },
1222 { "LightSalmon", 0xFFA07A },
1223 { "LightSeaGreen", 0x20B2AA },
1224 { "LightSkyBlue", 0x87CEFA },
1225 { "LightSlateGray", 0x778899 },
1226 { "LightSlateGrey", 0x778899 },
1227 { "LightSteelBlue", 0xB0C4DE },
1228 { "LightYellow", 0xFFFFE0 },
1229 { "Lime", 0x00FF00 },
1230 { "LimeGreen", 0x32CD32 },
1231 { "Linen", 0xFAF0E6 },
1232 { "Magenta", 0xFF00FF },
1233 { "Maroon", 0x800000 },
1234 { "MediumAquaMarine", 0x66CDAA },
1235 { "MediumBlue", 0x0000CD },
1236 { "MediumOrchid", 0xBA55D3 },
1237 { "MediumPurple", 0x9370D8 },
1238 { "MediumSeaGreen", 0x3CB371 },
1239 { "MediumSlateBlue", 0x7B68EE },
1240 { "MediumSpringGreen", 0x00FA9A },
1241 { "MediumTurquoise", 0x48D1CC },
1242 { "MediumVioletRed", 0xC71585 },
1243 { "MidnightBlue", 0x191970 },
1244 { "MintCream", 0xF5FFFA },
1245 { "MistyRose", 0xFFE4E1 },
1246 { "Moccasin", 0xFFE4B5 },
1247 { "NavajoWhite", 0xFFDEAD },
1248 { "Navy", 0x000080 },
1249 { "OldLace", 0xFDF5E6 },
1250 { "Olive", 0x808000 },
1251 { "OliveDrab", 0x6B8E23 },
1252 { "Orange", 0xFFA500 },
1253 { "OrangeRed", 0xFF4500 },
1254 { "Orchid", 0xDA70D6 },
1255 { "PaleGoldenRod", 0xEEE8AA },
1256 { "PaleGreen", 0x98FB98 },
1257 { "PaleTurquoise", 0xAFEEEE },
1258 { "PaleVioletRed", 0xD87093 },
1259 { "PapayaWhip", 0xFFEFD5 },
1260 { "PeachPuff", 0xFFDAB9 },
1261 { "Peru", 0xCD853F },
1262 { "Pink", 0xFFC0CB },
1263 { "Plum", 0xDDA0DD },
1264 { "PowderBlue", 0xB0E0E6 },
1265 { "Purple", 0x800080 },
1266 { "Red", 0xFF0000 },
1267 { "RosyBrown", 0xBC8F8F },
1268 { "RoyalBlue", 0x4169E1 },
1269 { "SaddleBrown", 0x8B4513 },
1270 { "Salmon", 0xFA8072 },
1271 { "SandyBrown", 0xF4A460 },
1272 { "SeaGreen", 0x2E8B57 },
1273 { "SeaShell", 0xFFF5EE },
1274 { "Sienna", 0xA0522D },
1275 { "Silver", 0xC0C0C0 },
1276 { "SkyBlue", 0x87CEEB },
1277 { "SlateBlue", 0x6A5ACD },
1278 { "SlateGray", 0x708090 },
1279 { "SlateGrey", 0x708090 },
1280 { "Snow", 0xFFFAFA },
1281 { "SpringGreen", 0x00FF7F },
1282 { "SteelBlue", 0x4682B4 },
1283 { "Tan", 0xD2B48C },
1284 { "Teal", 0x008080 },
1285 { "Thistle", 0xD8BFD8 },
1286 { "Tomato", 0xFF6347 },
1287 { "Turquoise", 0x40E0D0 },
1288 { "Violet", 0xEE82EE },
1289 { "Wheat", 0xF5DEB3 },
1290 { "White", 0xFFFFFF },
1291 { "WhiteSmoke", 0xF5F5F5 },
1292 { "Yellow", 0xFFFF00 },
1293 { "YellowGreen", 0x9ACD32 },
1298 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1299 font_stack_t **p_fonts )
1302 char *psz_fontname = NULL;
1303 uint32_t i_font_color = 0xffffff;
1304 int i_font_alpha = 0;
1305 uint32_t i_karaoke_bg_color = 0x00ffffff;
1306 int i_font_size = 24;
1308 /* Default all attributes to the top font in the stack -- in case not
1309 * all attributes are specified in the sub-font
1311 if( VLC_SUCCESS == PeekFont( p_fonts,
1315 &i_karaoke_bg_color ))
1317 psz_fontname = strdup( psz_fontname );
1318 i_font_size = i_font_size;
1320 i_font_alpha = (i_font_color >> 24) & 0xff;
1321 i_font_color &= 0x00ffffff;
1323 const char *name, *value;
1324 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1326 if( !strcasecmp( "face", name ) )
1328 free( psz_fontname );
1329 psz_fontname = strdup( value );
1331 else if( !strcasecmp( "size", name ) )
1333 if( ( *value == '+' ) || ( *value == '-' ) )
1335 int i_value = atoi( value );
1337 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1338 i_font_size += ( i_value * i_font_size ) / 10;
1339 else if( i_value < -5 )
1340 i_font_size = - i_value;
1341 else if( i_value > 5 )
1342 i_font_size = i_value;
1345 i_font_size = atoi( value );
1347 else if( !strcasecmp( "color", name ) )
1349 if( value[0] == '#' )
1351 i_font_color = strtol( value + 1, NULL, 16 );
1352 i_font_color &= 0x00ffffff;
1356 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
1358 if( !strncasecmp( value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
1360 i_font_color = p_html_colors[i].i_value;
1366 else if( !strcasecmp( "alpha", name ) && ( value[0] == '#' ) )
1368 i_font_alpha = strtol( value + 1, NULL, 16 );
1369 i_font_alpha &= 0xff;
1372 rv = PushFont( p_fonts,
1375 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1376 i_karaoke_bg_color );
1378 free( psz_fontname );
1383 /* Turn any multiple-whitespaces into single spaces */
1384 static void HandleWhiteSpace( char *psz_node )
1386 char *s = strpbrk( psz_node, "\t\r\n " );
1389 int i_whitespace = strspn( s, "\t\r\n " );
1391 if( i_whitespace > 1 )
1394 strlen( s ) - i_whitespace + 1 );
1397 s = strpbrk( s, "\t\r\n " );
1402 static text_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1403 font_stack_t **p_fonts,
1406 char *psz_fontname = NULL;
1407 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1408 uint32_t i_karaoke_bg_color = i_font_color;
1409 int i_font_size = p_sys->i_font_size;
1411 if( PeekFont( p_fonts, &psz_fontname, &i_font_size,
1412 &i_font_color, &i_karaoke_bg_color ) )
1415 return CreateStyle( psz_fontname, i_font_size, i_font_color,
1420 static unsigned SetupText( filter_t *p_filter,
1421 uint32_t *psz_text_out,
1422 text_style_t **pp_styles,
1423 uint32_t *pi_k_dates,
1425 const char *psz_text_in,
1426 text_style_t *p_style,
1429 size_t i_string_length;
1431 size_t i_string_bytes;
1432 #if defined(WORDS_BIGENDIAN)
1433 uint32_t *psz_tmp = ToCharset( "UCS-4BE", psz_text_in, &i_string_bytes );
1435 uint32_t *psz_tmp = ToCharset( "UCS-4LE", psz_text_in, &i_string_bytes );
1439 memcpy( psz_text_out, psz_tmp, i_string_bytes );
1440 i_string_length = i_string_bytes / 4;
1445 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1446 i_string_length = 0;
1449 if( i_string_length > 0 )
1451 for( unsigned i = 0; i < i_string_length; i++ )
1452 pp_styles[i] = p_style;
1456 text_style_Delete( p_style );
1458 if( i_string_length > 0 && pi_k_dates )
1460 for( unsigned i = 0; i < i_string_length; i++ )
1461 pi_k_dates[i] = i_k_date;
1463 return i_string_length;
1466 static int ProcessNodes( filter_t *p_filter,
1468 text_style_t **pp_styles,
1469 uint32_t *pi_k_dates,
1471 xml_reader_t *p_xml_reader,
1472 text_style_t *p_font_style )
1474 int rv = VLC_SUCCESS;
1475 filter_sys_t *p_sys = p_filter->p_sys;
1476 int i_text_length = 0;
1477 font_stack_t *p_fonts = NULL;
1478 uint32_t i_k_date = 0;
1480 int i_style_flags = 0;
1484 rv = PushFont( &p_fonts,
1485 p_font_style->psz_fontname,
1486 p_font_style->i_font_size,
1487 (p_font_style->i_font_color & 0xffffff) |
1488 ((p_font_style->i_font_alpha & 0xff) << 24),
1489 (p_font_style->i_karaoke_background_color & 0xffffff) |
1490 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1492 i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
1500 rv = PushFont( &p_fonts,
1501 p_sys->psz_fontfamily,
1503 (p_sys->i_font_color & 0xffffff) |
1504 (((255-p_sys->i_font_opacity) & 0xff) << 24),
1509 if( rv != VLC_SUCCESS )
1515 while ( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
1519 case XML_READER_ENDELEM:
1520 if( !strcasecmp( "font", node ) )
1521 PopFont( &p_fonts );
1522 else if( !strcasecmp( "b", node ) )
1523 i_style_flags &= ~STYLE_BOLD;
1524 else if( !strcasecmp( "i", node ) )
1525 i_style_flags &= ~STYLE_ITALIC;
1526 else if( !strcasecmp( "u", node ) )
1527 i_style_flags &= ~STYLE_UNDERLINE;
1528 else if( !strcasecmp( "s", node ) )
1529 i_style_flags &= ~STYLE_STRIKEOUT;
1532 case XML_READER_STARTELEM:
1533 if( !strcasecmp( "font", node ) )
1534 HandleFontAttributes( p_xml_reader, &p_fonts );
1535 else if( !strcasecmp( "b", node ) )
1536 i_style_flags |= STYLE_BOLD;
1537 else if( !strcasecmp( "i", node ) )
1538 i_style_flags |= STYLE_ITALIC;
1539 else if( !strcasecmp( "u", node ) )
1540 i_style_flags |= STYLE_UNDERLINE;
1541 else if( !strcasecmp( "s", node ) )
1542 i_style_flags |= STYLE_STRIKEOUT;
1543 else if( !strcasecmp( "br", node ) )
1545 i_text_length += SetupText( p_filter,
1546 &psz_text[i_text_length],
1547 &pp_styles[i_text_length],
1548 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1550 GetStyleFromFontStack( p_sys,
1555 else if( !strcasecmp( "k", node ) )
1558 const char *name, *value;
1559 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1561 if( !strcasecmp( "t", name ) && value )
1562 i_k_date += atoi( value );
1567 case XML_READER_TEXT:
1569 char *psz_node = strdup( node );
1570 if( unlikely(!psz_node) )
1573 HandleWhiteSpace( psz_node );
1574 resolve_xml_special_chars( psz_node );
1576 i_text_length += SetupText( p_filter,
1577 &psz_text[i_text_length],
1578 &pp_styles[i_text_length],
1579 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1581 GetStyleFromFontStack( p_sys,
1591 *pi_len = i_text_length;
1593 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1598 static void FreeLine( line_desc_t *p_line )
1600 for( int i = 0; p_line->pp_glyphs && p_line->pp_glyphs[i] != NULL; i++ )
1601 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[i] );
1603 free( p_line->pp_glyphs );
1604 free( p_line->p_glyph_pos );
1605 free( p_line->p_fg_rgb );
1606 free( p_line->p_bg_rgb );
1607 free( p_line->p_fg_bg_ratio );
1608 free( p_line->pi_underline_offset );
1609 free( p_line->pi_underline_thickness );
1613 static void FreeLines( line_desc_t *p_lines )
1615 for( line_desc_t *p_line = p_lines; p_line != NULL; )
1617 line_desc_t *p_next = p_line->p_next;
1623 static line_desc_t *NewLine( int i_count )
1625 line_desc_t *p_line = malloc( sizeof(*p_line) );
1630 p_line->i_width = 0;
1631 p_line->i_height = 0;
1632 p_line->i_alpha = 0xff;
1634 p_line->p_next = NULL;
1636 p_line->pp_glyphs = calloc( i_count + 1, sizeof(*p_line->pp_glyphs) );
1637 p_line->p_glyph_pos = calloc( i_count + 1, sizeof(*p_line->p_glyph_pos) );
1638 p_line->p_fg_rgb = calloc( i_count + 1, sizeof(*p_line->p_fg_rgb) );
1639 p_line->p_bg_rgb = calloc( i_count + 1, sizeof(*p_line->p_bg_rgb) );
1640 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof(*p_line->p_fg_bg_ratio) );
1641 p_line->pi_underline_offset = calloc( i_count + 1, sizeof(*p_line->pi_underline_offset) );
1642 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof(*p_line->pi_underline_thickness) );
1644 if( !p_line->pp_glyphs || !p_line->p_glyph_pos ||
1645 !p_line->p_fg_rgb || !p_line->p_bg_rgb || !p_line->p_fg_bg_ratio ||
1646 !p_line->pi_underline_offset || !p_line->pi_underline_thickness )
1651 p_line->pp_glyphs[0] = NULL;
1655 static int RenderTag( filter_t *p_filter, FT_Face p_face,
1656 const text_style_t *p_style,
1657 line_desc_t *p_line, uint32_t *psz_unicode,
1658 int *pi_pen_x, int i_pen_y, int *pi_start,
1659 FT_Vector *p_result )
1664 bool b_first_on_line = true;
1667 int i_pen_x_start = *pi_pen_x;
1669 uint32_t *psz_unicode_start = psz_unicode;
1671 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1673 /* Account for part of line already in position */
1674 for( i = 0; i<*pi_start; i++ )
1678 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1679 ft_glyph_bbox_pixels, &glyph_size );
1681 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1682 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1683 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1684 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1690 b_first_on_line = false;
1692 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1698 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1699 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1703 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1704 ft_kerning_default, &delta );
1705 *pi_pen_x += delta.x >> 6;
1707 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1708 p_line->p_glyph_pos[ i ].y = i_pen_y;
1710 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1713 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1717 "unable to render text FT_Load_Glyph returned %d", i_error );
1718 p_line->pp_glyphs[ i ] = NULL;
1719 return VLC_EGENERIC;
1723 /* Do synthetic styling now that Freetype supports it;
1724 * ie. if the font we have loaded is NOT already in the
1725 * style that the tags want, then switch it on; if they
1726 * are then don't. */
1727 if ((p_style->i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1728 FT_GlyphSlot_Embolden( p_face->glyph );
1729 if ((p_style->i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1730 FT_GlyphSlot_Oblique( p_face->glyph );
1732 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1736 "unable to render text FT_Get_Glyph returned %d", i_error );
1737 p_line->pp_glyphs[ i ] = NULL;
1738 return VLC_EGENERIC;
1740 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1741 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1744 FT_Done_Glyph( tmp_glyph );
1747 if( p_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
1749 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1750 p_face->size->metrics.y_scale));
1751 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1752 p_face->size->metrics.y_scale));
1754 p_line->pi_underline_offset[ i ] =
1755 ( aOffset < 0 ) ? -aOffset : aOffset;
1756 p_line->pi_underline_thickness[ i ] =
1757 ( aSize < 0 ) ? -aSize : aSize;
1758 if( p_style->i_style_flags & STYLE_STRIKEOUT )
1760 /* Move the baseline to make it strikethrough instead of
1761 * underline. That means that strikethrough takes precedence
1763 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1764 p_face->size->metrics.y_scale));
1766 p_line->pi_underline_offset[ i ] -=
1767 ( aDescent < 0 ) ? -aDescent : aDescent;
1771 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1772 p_line->p_fg_rgb[ i ] = p_style->i_font_color;
1773 p_line->p_bg_rgb[ i ] = p_style->i_karaoke_background_color;
1774 p_line->p_fg_bg_ratio[ i ] = 0x00;
1776 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1777 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1778 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1780 for( ; i >= *pi_start; i-- )
1781 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1784 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1788 if( psz_unicode == psz_unicode_start )
1790 if( b_first_on_line )
1792 msg_Warn( p_filter, "unbreakable string" );
1793 p_line->pp_glyphs[ i ] = NULL;
1794 return VLC_EGENERIC;
1796 *pi_pen_x = i_pen_x_start;
1798 p_line->i_width = line.xMax;
1799 p_line->i_height = __MAX( p_line->i_height,
1800 p_face->size->metrics.height >> 6 );
1801 p_line->pp_glyphs[ i ] = NULL;
1803 p_result->x = __MAX( p_result->x, line.xMax );
1804 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1805 i_yMax - i_yMin ) );
1810 *psz_unicode = '\n';
1812 psz_unicode = psz_unicode_start;
1813 *pi_pen_x = i_pen_x_start;
1821 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1822 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1824 i_previous = i_glyph_index;
1825 *pi_pen_x += p_face->glyph->advance.x >> 6;
1828 p_line->i_width = line.xMax;
1829 p_line->i_height = __MAX( p_line->i_height,
1830 p_face->size->metrics.height >> 6 );
1831 p_line->pp_glyphs[ i ] = NULL;
1833 p_result->x = __MAX( p_result->x, line.xMax );
1834 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1835 line.yMax - line.yMin ) );
1839 /* Get rid of any text processed - if necessary repositioning
1840 * at the start of a new line of text
1844 *psz_unicode_start = '\0';
1846 else if( psz_unicode > psz_unicode_start )
1848 for( i=0; psz_unicode[ i ]; i++ )
1849 psz_unicode_start[ i ] = psz_unicode[ i ];
1850 psz_unicode_start[ i ] = '\0';
1856 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1858 for( int k = 0; k < p_sys->i_font_attachments; k++ )
1860 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1862 FT_Face p_face = NULL;
1864 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1872 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD) ? STYLE_BOLD : 0) |
1873 ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1874 if( !strcasecmp( p_face->family_name, p_style->psz_fontname ) &&
1875 (p_style->i_style_flags & (STYLE_BOLD | STYLE_BOLD)) == i_style_received )
1878 FT_Done_Face( p_face );
1886 static FT_Face LoadFace( filter_t *p_filter,
1887 const text_style_t *p_style )
1889 filter_sys_t *p_sys = p_filter->p_sys;
1891 /* Look for a match amongst our attachments first */
1892 FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1894 /* Load system wide font otheriwse */
1899 #ifdef HAVE_FONTCONFIG
1900 psz_fontfile = FontConfig_Select( NULL,
1901 p_style->psz_fontname,
1902 (p_style->i_style_flags & STYLE_BOLD) != 0,
1903 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1906 #elif defined( WIN32 )
1907 psz_fontfile = Win32_Select( p_filter,
1908 p_style->psz_fontname,
1909 (p_style->i_style_flags & STYLE_BOLD) != 0,
1910 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1914 psz_fontfile = NULL;
1919 if( *psz_fontfile == '\0' )
1922 "We were not able to find a matching font: \"%s\" (%s %s),"
1923 " so using default font",
1924 p_style->psz_fontname,
1925 (p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
1926 (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1931 if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1934 free( psz_fontfile );
1939 if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1941 /* We've loaded a font face which is unhelpful for actually
1942 * rendering text - fallback to the default one.
1944 FT_Done_Face( p_face );
1950 static int ProcessLines( filter_t *p_filter,
1951 line_desc_t **pp_lines,
1952 FT_Vector *p_result,
1955 text_style_t **pp_styles,
1956 uint32_t *pi_k_dates,
1959 filter_sys_t *p_sys = p_filter->p_sys;
1960 uint32_t *p_fribidi_string = NULL;
1961 text_style_t **pp_fribidi_styles = NULL;
1962 int *p_new_positions = NULL;
1963 uint8_t *pi_karaoke_bar = NULL;
1968 pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1969 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1970 * we just won't be able to display the progress bar; at least we'll
1975 #if defined(HAVE_FRIBIDI)
1977 int *p_old_positions;
1979 int start_pos, pos = 0;
1981 pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1983 p_fribidi_string = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1984 p_old_positions = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1985 p_new_positions = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1986 p_levels = malloc( (i_len + 1) * sizeof(*p_levels) );
1988 if( ! pp_fribidi_styles ||
1989 ! p_fribidi_string ||
1990 ! p_old_positions ||
1991 ! p_new_positions ||
1995 free( p_old_positions );
1996 free( p_new_positions );
1997 free( p_fribidi_string );
1998 free( pp_fribidi_styles );
1999 free( pi_karaoke_bar );
2003 /* Do bidi conversion line-by-line */
2006 while(pos < i_len) {
2007 if (psz_text[pos] != '\n')
2009 p_fribidi_string[pos] = psz_text[pos];
2010 pp_fribidi_styles[pos] = pp_styles[pos];
2011 p_new_positions[pos] = pos;
2016 while(pos < i_len) {
2017 if (psz_text[pos] == '\n')
2021 if (pos > start_pos)
2023 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
2024 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2026 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
2028 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2029 pos - start_pos, &base_dir,
2030 (FriBidiChar*)p_fribidi_string + start_pos,
2031 p_new_positions + start_pos,
2033 p_levels + start_pos );
2034 for( int j = start_pos; j < pos; j++ )
2036 pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
2037 p_new_positions[ j ] += start_pos;
2041 p_fribidi_string[ i_len ] = 0;
2042 free( p_old_positions );
2045 pp_styles = pp_fribidi_styles;
2046 psz_text = p_fribidi_string;
2049 /* Work out the karaoke */
2050 if( pi_karaoke_bar )
2052 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2053 for( int i = 0; i < i_len; i++ )
2055 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
2056 if( pi_k_dates[i] < i_elapsed )
2057 pi_karaoke_bar[i_bar] = 0x7f;
2059 pi_karaoke_bar[i_bar] = 0x00;
2062 free( p_new_positions );
2064 FT_Vector tmp_result;
2066 line_desc_t *p_line = NULL;
2067 line_desc_t *p_prev = NULL;
2073 p_result->x = p_result->y = 0;
2074 tmp_result.x = tmp_result.y = 0;
2077 for( uint32_t k = 0; k <= (uint32_t) i_len; k++ )
2079 if( ( k == (uint32_t) i_len ) ||
2081 !StyleEquals( pp_styles[ k ], pp_styles[ k - 1] ) ) )
2083 text_style_t *p_style = pp_styles[ k - 1 ];
2085 FT_Face p_face = LoadFace( p_filter, p_style );
2086 if( FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face,
2087 0, p_style->i_font_size ) )
2089 if( p_face ) FT_Done_Face( p_face );
2090 free( pp_fribidi_styles );
2091 free( p_fribidi_string );
2092 free( pi_karaoke_bar );
2093 return VLC_EGENERIC;
2096 uint32_t *psz_unicode = malloc( (k - i_prev + 1) * sizeof(*psz_unicode) );
2099 if( p_face ) FT_Done_Face( p_face );
2100 free( pp_fribidi_styles );
2101 free( psz_unicode );
2102 free( p_fribidi_string );
2103 free( pi_karaoke_bar );
2106 memcpy( psz_unicode, psz_text + i_prev,
2107 sizeof( uint32_t ) * ( k - i_prev ) );
2108 psz_unicode[ k - i_prev ] = 0;
2109 while( *psz_unicode )
2113 if( !(p_line = NewLine( i_len - i_prev)) )
2115 if( p_face ) FT_Done_Face( p_face );
2116 free( pp_fribidi_styles );
2117 free( psz_unicode );
2118 free( p_fribidi_string );
2119 free( pi_karaoke_bar );
2122 p_line->i_alpha = p_style->i_font_alpha & 0xff;
2124 i_pen_y += tmp_result.y;
2128 if( p_prev ) p_prev->p_next = p_line;
2129 else *pp_lines = p_line;
2132 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2134 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2135 &tmp_result ) != VLC_SUCCESS )
2137 if( p_face ) FT_Done_Face( p_face );
2138 free( pp_fribidi_styles );
2139 free( psz_unicode );
2140 free( p_fribidi_string );
2141 free( pi_karaoke_bar );
2142 return VLC_EGENERIC;
2147 p_result->x = __MAX( p_result->x, tmp_result.x );
2148 p_result->y += tmp_result.y;
2153 if( *psz_unicode == '\n')
2157 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2159 *c_ptr = *(c_ptr+1);
2164 free( psz_unicode );
2165 if( p_face ) FT_Done_Face( p_face );
2169 free( pp_fribidi_styles );
2170 free( p_fribidi_string );
2174 p_result->x = __MAX( p_result->x, tmp_result.x );
2175 p_result->y += tmp_result.y;
2178 if( pi_karaoke_bar )
2181 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2183 for( uint32_t k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2185 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2189 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2191 /* 100% BG colour will render faster if we
2192 * instead make it 100% FG colour, so leave
2193 * the ratio alone and copy the value across
2195 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2199 if( pi_karaoke_bar[ i ] & 0x80 )
2201 /* Swap Left and Right sides over for Right aligned
2202 * language text (eg. Arabic, Hebrew)
2204 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2206 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2207 p_line->p_bg_rgb[ k ] = i_tmp;
2209 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2212 /* Jump over the '\n' at the line-end */
2215 free( pi_karaoke_bar );
2222 * This function renders a text subpicture region into another one.
2223 * It also calculates the size needed for this string, and renders the
2224 * needed glyphs into memory. It is used as pf_add_string callback in
2225 * the vout method by this module
2227 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
2228 subpicture_region_t *p_region_in, bool b_html )
2230 filter_sys_t *p_sys = p_filter->p_sys;
2233 return VLC_EGENERIC;
2234 if( b_html && !p_region_in->psz_html )
2235 return VLC_EGENERIC;
2236 if( !b_html && !p_region_in->psz_text )
2237 return VLC_EGENERIC;
2239 const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
2240 : p_region_in->psz_text );
2242 uint32_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
2243 text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
2244 if( !psz_text || !pp_styles )
2248 return VLC_EGENERIC;
2251 /* Reset the default fontsize in case screen metrics have changed */
2252 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2255 int rv = VLC_SUCCESS;
2256 int i_text_length = 0;
2257 FT_Vector result = {0, 0};
2258 line_desc_t *p_lines = NULL;
2260 uint32_t *pi_k_durations = NULL;
2265 stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2266 (uint8_t *) p_region_in->psz_html,
2267 strlen( p_region_in->psz_html ),
2269 if( unlikely(p_sub == NULL) )
2272 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2274 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2276 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2277 p_filter->p_sys->p_xml = p_xml_reader;
2284 /* Look for Root Node */
2287 if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2289 if( strcasecmp( "karaoke", node ) == 0 )
2291 pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
2293 else if( strcasecmp( "text", node ) != 0 )
2295 /* Only text and karaoke tags are supported */
2296 msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2303 msg_Err( p_filter, "Malformed HTML subtitle" );
2309 rv = ProcessNodes( p_filter,
2310 psz_text, pp_styles, pi_k_durations, &i_text_length,
2311 p_xml_reader, p_region_in->p_style );
2315 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2317 stream_Delete( p_sub );
2322 text_style_t *p_style;
2323 if( p_region_in->p_style )
2324 p_style = CreateStyle( p_region_in->p_style->psz_fontname,
2325 p_region_in->p_style->i_font_size,
2326 (p_region_in->p_style->i_font_color & 0xffffff) |
2327 ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2329 p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2334 p_style = CreateStyle( p_sys->psz_fontfamily,
2336 (p_sys->i_font_color & 0xffffff) |
2337 (((255-p_sys->i_font_opacity) & 0xff) << 24),
2340 i_text_length = SetupText( p_filter,
2344 p_region_in->psz_text, p_style, 0 );
2347 if( !rv && i_text_length > 0 )
2349 rv = ProcessLines( p_filter,
2351 psz_text, pp_styles, pi_k_durations, i_text_length );
2354 p_region_out->i_x = p_region_in->i_x;
2355 p_region_out->i_y = p_region_in->i_y;
2357 /* Don't attempt to render text that couldn't be layed out
2359 if( !rv && i_text_length > 0 )
2361 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2362 RenderYUVP( p_filter, p_region_out, p_lines,
2363 result.x, result.y );
2365 RenderYUVA( p_filter, p_region_out, p_lines,
2366 result.x, result.y );
2369 /* With karaoke, we're going to have to render the text a number
2370 * of times to show the progress marker on the text.
2372 if( pi_k_durations )
2373 var_SetBool( p_filter, "text-rerender", true );
2376 FreeLines( p_lines );
2379 for( int i = 0; i < i_text_length; i++ )
2381 if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2382 text_style_Delete( pp_styles[i] );
2385 free( pi_k_durations );
2390 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2391 subpicture_region_t *p_region_in )
2393 return RenderCommon( p_filter, p_region_out, p_region_in, false );
2398 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2399 subpicture_region_t *p_region_in )
2401 return RenderCommon( p_filter, p_region_out, p_region_in, true );
2406 /*****************************************************************************
2407 * Create: allocates osd-text video thread output method
2408 *****************************************************************************
2409 * This function allocates and initializes a Clone vout method.
2410 *****************************************************************************/
2411 static int Create( vlc_object_t *p_this )
2413 filter_t *p_filter = (filter_t *)p_this;
2414 filter_sys_t *p_sys;
2415 char *psz_fontfile = NULL;
2416 char *psz_fontfamily = NULL;
2417 int i_error = 0, fontindex = 0;
2419 /* Allocate structure */
2420 p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2424 p_sys->psz_fontfamily = NULL;
2426 p_sys->p_xml = NULL;
2429 p_sys->p_library = 0;
2430 p_sys->i_font_size = 0;
2431 p_sys->i_display_height = 0;
2433 var_Create( p_filter, "freetype-rel-fontsize",
2434 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
2436 psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
2437 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2438 p_sys->i_effect = var_InheritInteger( p_filter, "freetype-effect" );
2439 p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
2440 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
2441 p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2442 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
2445 /* Get Windows Font folder */
2446 wchar_t wdir[MAX_PATH];
2447 if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2449 GetWindowsDirectoryW( wdir, MAX_PATH );
2450 wcscat( wdir, L"\\fonts" );
2452 p_sys->psz_win_fonts_path = FromWide( wdir );
2455 /* Set default psz_fontfamily */
2456 if( !psz_fontfamily || !*psz_fontfamily )
2458 free( psz_fontfamily );
2460 psz_fontfamily = strdup( DEFAULT_FAMILY );
2463 if( asprintf( &psz_fontfamily, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2466 psz_fontfamily = strdup( DEFAULT_FONT_FILE );
2468 msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
2472 /* Set the current font file */
2473 p_sys->psz_fontfamily = psz_fontfamily;
2475 #ifdef HAVE_FONTCONFIG
2476 FontConfig_BuildCache( p_filter );
2479 psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
2480 p_sys->i_default_font_size, &fontindex );
2481 #elif defined(WIN32)
2482 psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
2483 p_sys->i_default_font_size, &fontindex );
2486 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
2488 /* If nothing is found, use the default family */
2490 psz_fontfile = strdup( psz_fontfamily );
2492 #else /* !HAVE_STYLES */
2493 /* Use the default file */
2494 psz_fontfile = psz_fontfamily;
2498 i_error = FT_Init_FreeType( &p_sys->p_library );
2501 msg_Err( p_filter, "couldn't initialize freetype" );
2505 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2506 fontindex, &p_sys->p_face );
2508 if( i_error == FT_Err_Unknown_File_Format )
2510 msg_Err( p_filter, "file %s have unknown format",
2511 psz_fontfile ? psz_fontfile : "(null)" );
2516 msg_Err( p_filter, "failed to load font file %s",
2517 psz_fontfile ? psz_fontfile : "(null)" );
2521 free( psz_fontfile );
2524 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2527 msg_Err( p_filter, "font has no unicode translation table" );
2531 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2534 p_sys->pp_font_attachments = NULL;
2535 p_sys->i_font_attachments = 0;
2537 p_filter->pf_render_text = RenderText;
2539 p_filter->pf_render_html = RenderHtml;
2541 p_filter->pf_render_html = NULL;
2544 LoadFontsFromAttachments( p_filter );
2549 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2550 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2552 free( psz_fontfile );
2554 free( psz_fontfamily );
2556 return VLC_EGENERIC;
2559 /*****************************************************************************
2560 * Destroy: destroy Clone video thread output method
2561 *****************************************************************************
2562 * Clean up all data and library connections
2563 *****************************************************************************/
2564 static void Destroy( vlc_object_t *p_this )
2566 filter_t *p_filter = (filter_t *)p_this;
2567 filter_sys_t *p_sys = p_filter->p_sys;
2569 if( p_sys->pp_font_attachments )
2571 for( int k = 0; k < p_sys->i_font_attachments; k++ )
2572 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2574 free( p_sys->pp_font_attachments );
2578 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2580 free( p_sys->psz_fontfamily );
2582 /* FcFini asserts calling the subfunction FcCacheFini()
2583 * even if no other library functions have been made since FcInit(),
2584 * so don't call it. */
2586 FT_Done_Face( p_sys->p_face );
2587 FT_Done_FreeType( p_sys->p_library );