1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include <vlc_stream.h>
39 #include <vlc_input.h>
40 #include <vlc_strings.h>
41 #include <vlc_dialog.h>
42 #include <vlc_memory.h>
47 #include <freetype/ftsynth.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
57 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
58 #define FC_DEFAULT_FONT "Arial Black"
59 #elif defined( SYS_BEOS )
60 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
61 #define FC_DEFAULT_FONT "Swiss"
62 #elif defined( WIN32 )
63 #define DEFAULT_FONT "" /* Default font found at run-time */
64 #define FC_DEFAULT_FONT "Arial"
65 #elif defined( HAVE_MAEMO )
66 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
67 #define FC_DEFAULT_FONT "Nokia Sans Bold"
69 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
70 #define FC_DEFAULT_FONT "Serif Bold"
73 #if defined(HAVE_FRIBIDI)
74 #include <fribidi/fribidi.h>
77 #ifdef HAVE_FONTCONFIG
78 #include <fontconfig/fontconfig.h>
80 #define DEFAULT_FONT FC_DEFAULT_FONT
85 /*****************************************************************************
87 *****************************************************************************/
88 static int Create ( vlc_object_t * );
89 static void Destroy( vlc_object_t * );
91 #define FONT_TEXT N_("Font")
93 #ifdef HAVE_FONTCONFIG
94 #define FONT_LONGTEXT N_("Font family for the font you want to use")
96 #define FONT_LONGTEXT N_("Font file for the font you want to use")
99 #define FONTSIZE_TEXT N_("Font size in pixels")
100 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
101 "that will be rendered on the video. " \
102 "If set to something different than 0 this option will override the " \
103 "relative font size." )
104 #define OPACITY_TEXT N_("Opacity")
105 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
106 "text that will be rendered on the video. 0 = transparent, " \
107 "255 = totally opaque. " )
108 #define COLOR_TEXT N_("Text default color")
109 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
110 "the video. This must be an hexadecimal (like HTML colors). The first two "\
111 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
112 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
113 #define FONTSIZER_TEXT N_("Relative font size")
114 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
115 "fonts that will be rendered on the video. If absolute font size is set, "\
116 "relative size will be overridden." )
118 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
119 static const char *const ppsz_sizes_text[] = {
120 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
121 #define YUVP_TEXT N_("Use YUVP renderer")
122 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
123 "This option is only needed if you want to encode into DVB subtitles" )
124 #define EFFECT_TEXT N_("Font Effect")
125 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
126 "text to improve its readability." )
128 #define EFFECT_BACKGROUND 1
129 #define EFFECT_OUTLINE 2
130 #define EFFECT_OUTLINE_FAT 3
132 static int const pi_effects[] = { 1, 2, 3 };
133 static const char *const ppsz_effects_text[] = {
134 N_("Background"),N_("Outline"), N_("Fat Outline") };
135 static const int pi_color_values[] = {
136 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
137 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
138 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
140 static const char *const ppsz_color_descriptions[] = {
141 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
142 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
143 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
146 set_shortname( N_("Text renderer"))
147 set_description( N_("Freetype2 font renderer") )
148 set_category( CAT_VIDEO )
149 set_subcategory( SUBCAT_VIDEO_SUBPIC )
151 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
154 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
155 FONTSIZE_LONGTEXT, true )
157 /* opacity valid on 0..255, with default 255 = fully opaque */
158 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
159 OPACITY_TEXT, OPACITY_LONGTEXT, true )
161 /* hook to the color values list, with default 0x00ffffff = white */
162 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
163 COLOR_LONGTEXT, false )
164 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
166 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
167 FONTSIZER_LONGTEXT, false )
168 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
169 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
170 EFFECT_LONGTEXT, false )
171 change_integer_list( pi_effects, ppsz_effects_text, NULL )
173 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
174 YUVP_LONGTEXT, true )
175 set_capability( "text renderer", 100 )
176 add_shortcut( "text" )
177 set_callbacks( Create, Destroy )
182 /*****************************************************************************
184 *****************************************************************************/
186 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
187 static int RenderText( filter_t *, subpicture_region_t *,
188 subpicture_region_t * );
189 #ifdef HAVE_FONTCONFIG
190 static int RenderHtml( filter_t *, subpicture_region_t *,
191 subpicture_region_t * );
192 static char *FontConfig_Select( FcConfig *, const char *,
197 static int LoadFontsFromAttachments( filter_t *p_filter );
199 static int GetFontSize( filter_t *p_filter );
200 static int SetFontSize( filter_t *, int );
201 static void YUVFromRGB( uint32_t i_argb,
202 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
204 typedef struct line_desc_t line_desc_t;
207 /** NULL-terminated list of glyphs making the string */
208 FT_BitmapGlyph *pp_glyphs;
209 /** list of relative positions for the glyphs */
210 FT_Vector *p_glyph_pos;
211 /** list of RGB information for styled text
212 * -- if the rendering mode supports it (RenderYUVA) and
213 * b_new_color_mode is set, then it becomes possible to
214 * have multicoloured text within the subtitles. */
217 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
218 bool b_new_color_mode;
219 /** underline information -- only supplied if text should be underlined */
220 int *pi_underline_offset;
221 uint16_t *pi_underline_thickness;
225 int i_red, i_green, i_blue;
230 static line_desc_t *NewLine( int );
235 uint32_t i_font_color; /* ARGB */
236 uint32_t i_karaoke_bg_color; /* ARGB */
244 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
245 static void FreeLines( line_desc_t * );
246 static void FreeLine( line_desc_t * );
248 /*****************************************************************************
249 * filter_sys_t: freetype local data
250 *****************************************************************************
251 * This structure is part of the video output thread descriptor.
252 * It describes the freetype specific properties of an output thread.
253 *****************************************************************************/
256 FT_Library p_library; /* handle to library */
257 FT_Face p_face; /* handle to face object */
259 uint8_t i_font_opacity;
264 int i_default_font_size;
265 int i_display_height;
266 #ifdef HAVE_FONTCONFIG
267 char* psz_fontfamily;
271 input_attachment_t **pp_font_attachments;
272 int i_font_attachments;
276 #define UCHAR uint32_t
277 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
278 #define TR_FONT_STYLE_PTR ft_style_t *
280 #include "text_renderer.h"
282 /*****************************************************************************
283 * Create: allocates osd-text video thread output method
284 *****************************************************************************
285 * This function allocates and initializes a Clone vout method.
286 *****************************************************************************/
287 static int Create( vlc_object_t *p_this )
289 filter_t *p_filter = (filter_t *)p_this;
291 char *psz_fontfile=NULL;
292 char *psz_fontfamily=NULL;
293 int i_error,fontindex;
295 #ifdef HAVE_FONTCONFIG
296 FcPattern *fontpattern = NULL, *fontmatch = NULL;
297 /* Initialise result to Match, as fontconfig doesnt
298 * really set this other than some error-cases */
299 FcResult fontresult = FcResultMatch;
303 /* Allocate structure */
304 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
307 #ifdef HAVE_FONTCONFIG
308 p_sys->psz_fontfamily = NULL;
312 p_sys->p_library = 0;
313 p_sys->i_font_size = 0;
314 p_sys->i_display_height = 0;
316 var_Create( p_filter, "freetype-rel-fontsize",
317 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
319 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
320 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
321 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
322 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
323 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
324 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
325 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
328 if( !psz_fontfamily || !*psz_fontfamily )
330 free( psz_fontfamily );
331 #ifdef HAVE_FONTCONFIG
332 psz_fontfamily=strdup( DEFAULT_FONT );
334 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
335 if( !psz_fontfamily )
338 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
339 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
341 strcpy( psz_fontfamily, DEFAULT_FONT );
343 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
347 #ifdef HAVE_FONTCONFIG
348 msg_Dbg( p_filter, "Building font databases.");
353 dialog_progress_bar_t *p_dialog = NULL;
354 FcConfig *fcConfig = FcInitLoadConfig();
356 p_dialog = dialog_ProgressCreate( p_filter,
357 _("Building font cache"),
358 _("Please wait while your font cache is rebuilt.\n"
359 "This should take less than a few minutes."), NULL );
362 dialog_ProgressSet( p_dialog, NULL, 0.5 );
364 FcConfigBuildFonts( fcConfig );
366 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
370 dialog_ProgressSet( p_dialog, NULL, 1.0 );
371 dialog_ProgressDestroy( p_dialog );
375 /* Lets find some fontfile from freetype-font variable family */
377 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
380 fontpattern = FcPatternCreate();
383 msg_Err( p_filter, "Creating fontpattern failed");
387 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
388 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
389 free( psz_fontsize );
391 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
393 msg_Err( p_filter, "FontSubstitute failed");
396 FcDefaultSubstitute( fontpattern );
398 /* testing fontresult here doesn't do any good really, but maybe it will
399 * in future as fontconfig code doesn't set it in all cases and just
400 * returns NULL or doesn't set to to Match on all Match cases.*/
401 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
402 if( !fontmatch || fontresult == FcResultNoMatch )
404 msg_Err( p_filter, "Fontmatching failed");
408 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
409 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
412 msg_Err( p_filter, "Failed to get fontfile");
416 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
417 psz_fontfile ? psz_fontfile : "(null)" );
418 p_sys->psz_fontfamily = strdup( psz_fontfamily );
422 psz_fontfile = psz_fontfamily;
426 i_error = FT_Init_FreeType( &p_sys->p_library );
429 msg_Err( p_filter, "couldn't initialize freetype" );
433 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
434 fontindex, &p_sys->p_face );
436 if( i_error == FT_Err_Unknown_File_Format )
438 msg_Err( p_filter, "file %s have unknown format",
439 psz_fontfile ? psz_fontfile : "(null)" );
444 msg_Err( p_filter, "failed to load font file %s",
445 psz_fontfile ? psz_fontfile : "(null)" );
449 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
452 msg_Err( p_filter, "font has no unicode translation table" );
456 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
458 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
461 p_sys->pp_font_attachments = NULL;
462 p_sys->i_font_attachments = 0;
464 p_filter->pf_render_text = RenderText;
465 #ifdef HAVE_FONTCONFIG
466 p_filter->pf_render_html = RenderHtml;
467 FcPatternDestroy( fontmatch );
468 FcPatternDestroy( fontpattern );
470 p_filter->pf_render_html = NULL;
473 free( psz_fontfamily );
474 LoadFontsFromAttachments( p_filter );
479 #ifdef HAVE_FONTCONFIG
480 if( fontmatch ) FcPatternDestroy( fontmatch );
481 if( fontpattern ) FcPatternDestroy( fontpattern );
486 dialog_ProgressDestroy( p_dialog );
489 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
490 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
491 free( psz_fontfamily );
496 /*****************************************************************************
497 * Destroy: destroy Clone video thread output method
498 *****************************************************************************
499 * Clean up all data and library connections
500 *****************************************************************************/
501 static void Destroy( vlc_object_t *p_this )
503 filter_t *p_filter = (filter_t *)p_this;
504 filter_sys_t *p_sys = p_filter->p_sys;
506 if( p_sys->pp_font_attachments )
510 for( k = 0; k < p_sys->i_font_attachments; k++ )
511 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
513 free( p_sys->pp_font_attachments );
516 #ifdef HAVE_FONTCONFIG
517 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
518 free( p_sys->psz_fontfamily );
521 /* FcFini asserts calling the subfunction FcCacheFini()
522 * even if no other library functions have been made since FcInit(),
523 * so don't call it. */
525 FT_Done_Face( p_sys->p_face );
526 FT_Done_FreeType( p_sys->p_library );
530 /*****************************************************************************
531 * Make any TTF/OTF fonts present in the attachments of the media file
532 * and store them for later use by the FreeType Engine
533 *****************************************************************************/
534 static int LoadFontsFromAttachments( filter_t *p_filter )
536 filter_sys_t *p_sys = p_filter->p_sys;
537 input_attachment_t **pp_attachments;
538 int i_attachments_cnt;
540 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
543 p_sys->i_font_attachments = 0;
544 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
545 if( !p_sys->pp_font_attachments )
548 for( int k = 0; k < i_attachments_cnt; k++ )
550 input_attachment_t *p_attach = pp_attachments[k];
552 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
553 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
554 p_attach->i_data > 0 && p_attach->p_data )
556 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
560 vlc_input_attachment_Delete( p_attach );
563 free( pp_attachments );
568 /*****************************************************************************
569 * Render: place string in picture
570 *****************************************************************************
571 * This function merges the previously rendered freetype glyphs into a picture
572 *****************************************************************************/
573 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
574 line_desc_t *p_line, int i_width, int i_height )
576 VLC_UNUSED(p_filter);
577 static const uint8_t pi_gamma[16] =
578 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
579 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
583 int i, x, y, i_pitch;
584 uint8_t i_y; /* YUV values, derived from incoming RGB */
587 /* Create a new subpicture region */
588 memset( &fmt, 0, sizeof(video_format_t) );
589 fmt.i_chroma = VLC_CODEC_YUVP;
590 fmt.i_width = fmt.i_visible_width = i_width + 4;
591 fmt.i_height = fmt.i_visible_height = i_height + 4;
592 if( p_region->fmt.i_visible_width > 0 )
593 fmt.i_visible_width = p_region->fmt.i_visible_width;
594 if( p_region->fmt.i_visible_height > 0 )
595 fmt.i_visible_height = p_region->fmt.i_visible_height;
596 fmt.i_x_offset = fmt.i_y_offset = 0;
598 assert( !p_region->p_picture );
599 p_region->p_picture = picture_NewFromFormat( &fmt );
600 if( !p_region->p_picture )
602 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
605 /* Calculate text color components */
606 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
607 25 * p_line->i_blue + 128) >> 8) + 16;
608 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
609 112 * p_line->i_blue + 128) >> 8) + 128;
610 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
611 18 * p_line->i_blue + 128) >> 8) + 128;
614 fmt.p_palette->i_entries = 16;
615 for( i = 0; i < 8; i++ )
617 fmt.p_palette->palette[i][0] = 0;
618 fmt.p_palette->palette[i][1] = 0x80;
619 fmt.p_palette->palette[i][2] = 0x80;
620 fmt.p_palette->palette[i][3] = pi_gamma[i];
621 fmt.p_palette->palette[i][3] =
622 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
624 for( i = 8; i < fmt.p_palette->i_entries; i++ )
626 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
627 fmt.p_palette->palette[i][1] = i_u;
628 fmt.p_palette->palette[i][2] = i_v;
629 fmt.p_palette->palette[i][3] = pi_gamma[i];
630 fmt.p_palette->palette[i][3] =
631 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
634 p_dst = p_region->p_picture->Y_PIXELS;
635 i_pitch = p_region->p_picture->Y_PITCH;
637 /* Initialize the region pixels */
638 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
640 for( ; p_line != NULL; p_line = p_line->p_next )
642 int i_glyph_tmax = 0;
643 int i_bitmap_offset, i_offset, i_align_offset = 0;
644 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
646 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
647 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
650 if( p_line->i_width < i_width )
652 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
654 i_align_offset = i_width - p_line->i_width;
656 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
658 i_align_offset = ( i_width - p_line->i_width ) / 2;
662 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
664 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
666 i_offset = ( p_line->p_glyph_pos[ i ].y +
667 i_glyph_tmax - p_glyph->top + 2 ) *
668 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
671 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
673 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
675 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
677 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
684 /* Outlining (find something better than nearest neighbour filtering ?) */
687 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
688 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
689 uint8_t left, current;
691 for( y = 1; y < (int)fmt.i_height - 1; y++ )
693 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
694 p_dst += p_region->p_picture->Y_PITCH;
697 for( x = 1; x < (int)fmt.i_width - 1; x++ )
700 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
701 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;
705 memset( p_top, 0, fmt.i_width );
711 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
712 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
713 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
714 int i_glyph_tmax, int i_align_offset,
715 uint8_t i_y, uint8_t i_u, uint8_t i_v,
716 subpicture_region_t *p_region)
720 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
722 p_dst_y = p_region->p_picture->Y_PIXELS;
723 p_dst_u = p_region->p_picture->U_PIXELS;
724 p_dst_v = p_region->p_picture->V_PIXELS;
725 p_dst_a = p_region->p_picture->A_PIXELS;
726 i_pitch = p_region->p_picture->A_PITCH;
728 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
729 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
731 for( y = 0; y < i_line_thickness; y++ )
733 int i_extra = p_this_glyph->bitmap.width;
737 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
738 (p_this_glyph_pos->x + p_this_glyph->left);
740 for( x = 0; x < i_extra; x++ )
744 /* break the underline around the tails of any glyphs which cross it */
745 /* Strikethrough doesn't get broken */
746 for( z = x - i_line_thickness;
747 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
750 if( p_next_glyph && ( z >= i_extra ) )
752 int i_row = i_line_offset + p_next_glyph->top + y;
754 if( ( p_next_glyph->bitmap.rows > i_row ) &&
755 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
760 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
762 int i_row = i_line_offset + p_this_glyph->top + y;
764 if( ( p_this_glyph->bitmap.rows > i_row ) &&
765 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
774 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
775 p_dst_u[i_offset+x] = i_u;
776 p_dst_v[i_offset+x] = i_v;
777 p_dst_a[i_offset+x] = 255;
784 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
786 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
787 int i_pitch = p_region->p_picture->A_PITCH;
790 for( ; p_line != NULL; p_line = p_line->p_next )
792 int i_glyph_tmax=0, i = 0;
793 int i_bitmap_offset, i_offset, i_align_offset = 0;
794 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
796 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
797 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
800 if( p_line->i_width < i_width )
802 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
804 i_align_offset = i_width - p_line->i_width;
806 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
808 i_align_offset = ( i_width - p_line->i_width ) / 2;
812 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
814 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
816 i_offset = ( p_line->p_glyph_pos[ i ].y +
817 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
818 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
819 i_align_offset +xoffset;
821 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
823 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
825 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
826 if( p_dst[i_offset+x] <
827 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
829 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
838 /*****************************************************************************
839 * Render: place string in picture
840 *****************************************************************************
841 * This function merges the previously rendered freetype glyphs into a picture
842 *****************************************************************************/
843 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
844 line_desc_t *p_line, int i_width, int i_height )
846 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
848 int i, x, y, i_pitch, i_alpha;
849 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
851 if( i_width == 0 || i_height == 0 )
854 /* Create a new subpicture region */
855 memset( &fmt, 0, sizeof(video_format_t) );
856 fmt.i_chroma = VLC_CODEC_YUVA;
857 fmt.i_width = fmt.i_visible_width = i_width + 6;
858 fmt.i_height = fmt.i_visible_height = i_height + 6;
859 if( p_region->fmt.i_visible_width > 0 )
860 fmt.i_visible_width = p_region->fmt.i_visible_width;
861 if( p_region->fmt.i_visible_height > 0 )
862 fmt.i_visible_height = p_region->fmt.i_visible_height;
863 fmt.i_x_offset = fmt.i_y_offset = 0;
865 p_region->p_picture = picture_NewFromFormat( &fmt );
866 if( !p_region->p_picture )
870 /* Calculate text color components */
871 YUVFromRGB( (p_line->i_red << 16) |
872 (p_line->i_green << 8) |
875 i_alpha = p_line->i_alpha;
877 p_dst_y = p_region->p_picture->Y_PIXELS;
878 p_dst_u = p_region->p_picture->U_PIXELS;
879 p_dst_v = p_region->p_picture->V_PIXELS;
880 p_dst_a = p_region->p_picture->A_PIXELS;
881 i_pitch = p_region->p_picture->A_PITCH;
883 /* Initialize the region pixels */
884 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
886 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
887 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
888 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
889 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
893 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
894 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
895 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
896 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
898 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
899 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
901 DrawBlack( p_line, i_width, p_region, 0, 0);
902 DrawBlack( p_line, i_width, p_region, -1, 0);
903 DrawBlack( p_line, i_width, p_region, 0, -1);
904 DrawBlack( p_line, i_width, p_region, 1, 0);
905 DrawBlack( p_line, i_width, p_region, 0, 1);
908 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
910 DrawBlack( p_line, i_width, p_region, -1, -1);
911 DrawBlack( p_line, i_width, p_region, -1, 1);
912 DrawBlack( p_line, i_width, p_region, 1, -1);
913 DrawBlack( p_line, i_width, p_region, 1, 1);
915 DrawBlack( p_line, i_width, p_region, -2, 0);
916 DrawBlack( p_line, i_width, p_region, 0, -2);
917 DrawBlack( p_line, i_width, p_region, 2, 0);
918 DrawBlack( p_line, i_width, p_region, 0, 2);
920 DrawBlack( p_line, i_width, p_region, -2, -2);
921 DrawBlack( p_line, i_width, p_region, -2, 2);
922 DrawBlack( p_line, i_width, p_region, 2, -2);
923 DrawBlack( p_line, i_width, p_region, 2, 2);
925 DrawBlack( p_line, i_width, p_region, -3, 0);
926 DrawBlack( p_line, i_width, p_region, 0, -3);
927 DrawBlack( p_line, i_width, p_region, 3, 0);
928 DrawBlack( p_line, i_width, p_region, 0, 3);
931 for( ; p_line != NULL; p_line = p_line->p_next )
933 int i_glyph_tmax = 0;
934 int i_bitmap_offset, i_offset, i_align_offset = 0;
935 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
937 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
938 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
941 if( p_line->i_width < i_width )
943 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
945 i_align_offset = i_width - p_line->i_width;
947 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
949 i_align_offset = ( i_width - p_line->i_width ) / 2;
953 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
955 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
957 i_offset = ( p_line->p_glyph_pos[ i ].y +
958 i_glyph_tmax - p_glyph->top + 3 ) *
959 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
962 if( p_line->b_new_color_mode )
964 /* Every glyph can (and in fact must) have its own color */
965 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
968 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
970 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
972 uint8_t i_y_local = i_y;
973 uint8_t i_u_local = i_u;
974 uint8_t i_v_local = i_v;
976 if( p_line->p_fg_bg_ratio != 0x00 )
978 int i_split = p_glyph->bitmap.width *
979 p_line->p_fg_bg_ratio[ i ] / 0x7f;
983 YUVFromRGB( p_line->p_bg_rgb[ i ],
984 &i_y_local, &i_u_local, &i_v_local );
988 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
990 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
991 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
993 p_dst_u[i_offset+x] = i_u;
994 p_dst_v[i_offset+x] = i_v;
996 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
997 p_dst_a[i_offset+x] = 0xff;
1000 i_offset += i_pitch;
1003 if( p_line->pi_underline_thickness[ i ] )
1005 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1006 p_line->pi_underline_offset[ i ],
1007 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1008 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1009 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1010 i_glyph_tmax, i_align_offset,
1017 /* Apply the alpha setting */
1018 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1019 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1025 * This function renders a text subpicture region into another one.
1026 * It also calculates the size needed for this string, and renders the
1027 * needed glyphs into memory. It is used as pf_add_string callback in
1028 * the vout method by this module
1030 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1031 subpicture_region_t *p_region_in )
1033 filter_sys_t *p_sys = p_filter->p_sys;
1034 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1035 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1036 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1037 int i_string_length;
1039 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1040 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1050 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1051 psz_string = p_region_in->psz_text;
1052 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1054 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1055 i_scale = val.i_int;
1057 if( p_region_in->p_style )
1059 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1060 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1061 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1065 i_font_color = p_sys->i_font_color;
1066 i_font_alpha = 255 - p_sys->i_font_opacity;
1067 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1070 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1071 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1072 SetFontSize( p_filter, i_font_size );
1074 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1075 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1076 i_blue = i_font_color & 0x000000FF;
1078 result.x = result.y = 0;
1079 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1081 psz_unicode = psz_unicode_orig =
1082 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1083 if( psz_unicode == NULL )
1085 #if defined(WORDS_BIGENDIAN)
1086 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1088 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1090 if( iconv_handle == (vlc_iconv_t)-1 )
1092 msg_Warn( p_filter, "unable to do conversion" );
1098 const char *p_in_buffer = psz_string;
1099 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1100 i_in_bytes = strlen( psz_string );
1101 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1102 i_out_bytes_left = i_out_bytes;
1103 p_out_buffer = (char *)psz_unicode;
1104 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1106 &p_out_buffer, &i_out_bytes_left );
1108 vlc_iconv_close( iconv_handle );
1112 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1113 "bytes left %u", (unsigned)i_in_bytes );
1116 *(uint32_t*)p_out_buffer = 0;
1117 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1120 #if defined(HAVE_FRIBIDI)
1122 uint32_t *p_fribidi_string;
1123 int32_t start_pos, pos = 0;
1125 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1126 if( !p_fribidi_string )
1129 /* Do bidi conversion line-by-line */
1130 while( pos < i_string_length )
1132 while( pos < i_string_length )
1134 i_char = psz_unicode[pos];
1135 if (i_char != '\r' && i_char != '\n')
1137 p_fribidi_string[pos] = i_char;
1141 while( pos < i_string_length )
1143 i_char = psz_unicode[pos];
1144 if (i_char == '\r' || i_char == '\n')
1148 if (pos > start_pos)
1150 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1151 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1154 (FriBidiChar*)p_fribidi_string + start_pos,
1159 free( psz_unicode_orig );
1160 psz_unicode = psz_unicode_orig = p_fribidi_string;
1161 p_fribidi_string[ i_string_length ] = 0;
1165 /* Calculate relative glyph positions and a bounding box for the
1167 if( !(p_line = NewLine( strlen( psz_string ))) )
1170 i_pen_x = i_pen_y = 0;
1172 psz_line_start = psz_unicode;
1174 #define face p_sys->p_face
1175 #define glyph face->glyph
1177 while( *psz_unicode )
1179 i_char = *psz_unicode++;
1180 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1185 if( i_char == '\n' )
1187 psz_line_start = psz_unicode;
1188 if( !(p_next = NewLine( strlen( psz_string ))) )
1190 p_line->p_next = p_next;
1191 p_line->i_width = line.xMax;
1192 p_line->i_height = face->size->metrics.height >> 6;
1193 p_line->pp_glyphs[ i ] = NULL;
1194 p_line->i_alpha = i_font_alpha;
1195 p_line->i_red = i_red;
1196 p_line->i_green = i_green;
1197 p_line->i_blue = i_blue;
1200 result.x = __MAX( result.x, line.xMax );
1201 result.y += face->size->metrics.height >> 6;
1204 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1205 i_pen_y += face->size->metrics.height >> 6;
1207 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1212 i_glyph_index = FT_Get_Char_Index( face, i_char );
1213 if( p_sys->i_use_kerning && i_glyph_index
1217 FT_Get_Kerning( face, i_previous, i_glyph_index,
1218 ft_kerning_default, &delta );
1219 i_pen_x += delta.x >> 6;
1222 p_line->p_glyph_pos[ i ].x = i_pen_x;
1223 p_line->p_glyph_pos[ i ].y = i_pen_y;
1224 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1227 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1231 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1234 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1238 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1239 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1242 FT_Done_Glyph( tmp_glyph );
1245 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1248 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1249 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1250 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1252 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1253 p_line->pp_glyphs[ i ] = NULL;
1255 p_line = NewLine( strlen( psz_string ));
1256 if( p_prev ) p_prev->p_next = p_line;
1257 else p_lines = p_line;
1259 uint32_t *psz_unicode_saved = psz_unicode;
1260 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1264 if( psz_unicode == psz_line_start )
1265 { /* try harder to break that line */
1266 psz_unicode = psz_unicode_saved;
1267 while( psz_unicode > psz_line_start &&
1268 *psz_unicode != '_' && *psz_unicode != '/' &&
1269 *psz_unicode != '\\' && *psz_unicode != '.' )
1274 if( psz_unicode == psz_line_start )
1276 msg_Warn( p_filter, "unbreakable string" );
1281 *psz_unicode = '\n';
1283 psz_unicode = psz_line_start;
1286 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1289 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1290 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1292 i_previous = i_glyph_index;
1293 i_pen_x += glyph->advance.x >> 6;
1297 p_line->i_width = line.xMax;
1298 p_line->i_height = face->size->metrics.height >> 6;
1299 p_line->pp_glyphs[ i ] = NULL;
1300 p_line->i_alpha = i_font_alpha;
1301 p_line->i_red = i_red;
1302 p_line->i_green = i_green;
1303 p_line->i_blue = i_blue;
1304 result.x = __MAX( result.x, line.xMax );
1305 result.y += line.yMax - line.yMin;
1310 p_region_out->i_x = p_region_in->i_x;
1311 p_region_out->i_y = p_region_in->i_y;
1313 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1314 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1316 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1318 free( psz_unicode_orig );
1319 FreeLines( p_lines );
1323 free( psz_unicode_orig );
1324 FreeLines( p_lines );
1325 return VLC_EGENERIC;
1328 #ifdef HAVE_FONTCONFIG
1329 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1330 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1331 bool b_italic, bool b_uline, bool b_through )
1333 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1337 p_style->i_font_size = i_font_size;
1338 p_style->i_font_color = i_font_color;
1339 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1340 p_style->b_italic = b_italic;
1341 p_style->b_bold = b_bold;
1342 p_style->b_underline = b_uline;
1343 p_style->b_through = b_through;
1345 p_style->psz_fontname = strdup( psz_fontname );
1350 static void DeleteStyle( ft_style_t *p_style )
1354 free( p_style->psz_fontname );
1359 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1366 if(( s1->i_font_size == s2->i_font_size ) &&
1367 ( s1->i_font_color == s2->i_font_color ) &&
1368 ( s1->b_italic == s2->b_italic ) &&
1369 ( s1->b_through == s2->b_through ) &&
1370 ( s1->b_bold == s2->b_bold ) &&
1371 ( s1->b_underline == s2->b_underline ) &&
1372 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1379 static void IconvText( filter_t *p_filter, const char *psz_string,
1380 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1382 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1384 /* If memory hasn't been allocated for our output string, allocate it here
1385 * - the calling function must now be responsible for freeing it.
1387 if( !*ppsz_unicode )
1388 *ppsz_unicode = (uint32_t *)
1389 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1391 /* We don't need to handle a NULL pointer in *ppsz_unicode
1392 * if we are instead testing for a non NULL value like we are here */
1396 #if defined(WORDS_BIGENDIAN)
1397 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1399 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1401 if( iconv_handle != (vlc_iconv_t)-1 )
1403 char *p_in_buffer, *p_out_buffer;
1404 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1405 i_in_bytes = strlen( psz_string );
1406 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1407 i_out_bytes_left = i_out_bytes;
1408 p_in_buffer = (char *) psz_string;
1409 p_out_buffer = (char *) *ppsz_unicode;
1410 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1411 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1413 vlc_iconv_close( iconv_handle );
1417 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1418 "bytes left %u", (unsigned)i_in_bytes );
1422 *(uint32_t*)p_out_buffer = 0;
1424 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1429 msg_Warn( p_filter, "unable to do conversion" );
1434 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1435 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1436 bool b_uline, bool b_through )
1438 ft_style_t *p_style = NULL;
1440 char *psz_fontname = NULL;
1441 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1442 uint32_t i_karaoke_bg_color = i_font_color;
1443 int i_font_size = p_sys->i_font_size;
1445 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1446 &i_font_color, &i_karaoke_bg_color ))
1448 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1449 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1454 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1455 bool b_uline, bool b_through, bool b_bold,
1456 bool b_italic, int i_karaoke_bgcolor,
1457 line_desc_t *p_line, uint32_t *psz_unicode,
1458 int *pi_pen_x, int i_pen_y, int *pi_start,
1459 FT_Vector *p_result )
1464 bool b_first_on_line = true;
1467 int i_pen_x_start = *pi_pen_x;
1469 uint32_t *psz_unicode_start = psz_unicode;
1471 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1473 /* Account for part of line already in position */
1474 for( i=0; i<*pi_start; i++ )
1478 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1479 ft_glyph_bbox_pixels, &glyph_size );
1481 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1482 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1483 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1484 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1490 b_first_on_line = false;
1492 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1498 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1499 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1503 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1504 ft_kerning_default, &delta );
1505 *pi_pen_x += delta.x >> 6;
1507 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1508 p_line->p_glyph_pos[ i ].y = i_pen_y;
1510 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1514 "unable to render text FT_Load_Glyph returned %d", i_error );
1515 p_line->pp_glyphs[ i ] = NULL;
1516 return VLC_EGENERIC;
1519 /* Do synthetic styling now that Freetype supports it;
1520 * ie. if the font we have loaded is NOT already in the
1521 * style that the tags want, then switch it on; if they
1522 * are then don't. */
1523 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1524 FT_GlyphSlot_Embolden( p_face->glyph );
1525 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1526 FT_GlyphSlot_Oblique( p_face->glyph );
1528 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1532 "unable to render text FT_Get_Glyph returned %d", i_error );
1533 p_line->pp_glyphs[ i ] = NULL;
1534 return VLC_EGENERIC;
1536 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1537 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1540 FT_Done_Glyph( tmp_glyph );
1543 if( b_uline || b_through )
1545 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1546 p_face->size->metrics.y_scale));
1547 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1548 p_face->size->metrics.y_scale));
1550 p_line->pi_underline_offset[ i ] =
1551 ( aOffset < 0 ) ? -aOffset : aOffset;
1552 p_line->pi_underline_thickness[ i ] =
1553 ( aSize < 0 ) ? -aSize : aSize;
1556 /* Move the baseline to make it strikethrough instead of
1557 * underline. That means that strikethrough takes precedence
1559 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1560 p_face->size->metrics.y_scale));
1562 p_line->pi_underline_offset[ i ] -=
1563 ( aDescent < 0 ) ? -aDescent : aDescent;
1567 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1568 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1569 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1570 p_line->p_fg_bg_ratio[ i ] = 0x00;
1572 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1573 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1574 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1576 for( ; i >= *pi_start; i-- )
1577 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1580 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1584 if( psz_unicode == psz_unicode_start )
1586 if( b_first_on_line )
1588 msg_Warn( p_filter, "unbreakable string" );
1589 p_line->pp_glyphs[ i ] = NULL;
1590 return VLC_EGENERIC;
1592 *pi_pen_x = i_pen_x_start;
1594 p_line->i_width = line.xMax;
1595 p_line->i_height = __MAX( p_line->i_height,
1596 p_face->size->metrics.height >> 6 );
1597 p_line->pp_glyphs[ i ] = NULL;
1599 p_result->x = __MAX( p_result->x, line.xMax );
1600 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1601 i_yMax - i_yMin ) );
1606 *psz_unicode = '\n';
1608 psz_unicode = psz_unicode_start;
1609 *pi_pen_x = i_pen_x_start;
1617 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1618 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1620 i_previous = i_glyph_index;
1621 *pi_pen_x += p_face->glyph->advance.x >> 6;
1624 p_line->i_width = line.xMax;
1625 p_line->i_height = __MAX( p_line->i_height,
1626 p_face->size->metrics.height >> 6 );
1627 p_line->pp_glyphs[ i ] = NULL;
1629 p_result->x = __MAX( p_result->x, line.xMax );
1630 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1631 line.yMax - line.yMin ) );
1635 /* Get rid of any text processed - if necessary repositioning
1636 * at the start of a new line of text
1640 *psz_unicode_start = '\0';
1642 else if( psz_unicode > psz_unicode_start )
1644 for( i=0; psz_unicode[ i ]; i++ )
1645 psz_unicode_start[ i ] = psz_unicode[ i ];
1646 psz_unicode_start[ i ] = '\0';
1652 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1653 uint32_t **psz_text_out, uint32_t *pi_runs,
1654 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1655 ft_style_t *p_style )
1657 uint32_t i_string_length = 0;
1659 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1660 *psz_text_out += i_string_length;
1662 if( ppp_styles && ppi_run_lengths )
1666 /* XXX this logic looks somewhat broken */
1670 *ppp_styles = realloc_or_free( *ppp_styles,
1671 *pi_runs * sizeof( ft_style_t * ) );
1673 else if( *pi_runs == 1 )
1675 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1678 /* We have just malloc'ed this memory successfully -
1679 * *pi_runs HAS to be within the memory area of *ppp_styles */
1682 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1686 /* XXX more iffy logic */
1688 if( *ppi_run_lengths )
1690 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1691 *pi_runs * sizeof( uint32_t ) );
1693 else if( *pi_runs == 1 )
1695 *ppi_run_lengths = (uint32_t *)
1696 malloc( *pi_runs * sizeof( uint32_t ) );
1699 /* same remarks here */
1700 if( *ppi_run_lengths )
1702 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1705 /* If we couldn't use the p_style argument due to memory allocation
1706 * problems above, release it here.
1708 if( p_style ) DeleteStyle( p_style );
1711 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1715 for( k=0; k < p_sys->i_font_attachments; k++ )
1717 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1719 FT_Face p_face = NULL;
1721 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1729 bool match = !strcasecmp( p_face->family_name,
1730 p_style->psz_fontname );
1732 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1733 match = match && p_style->b_bold;
1735 match = match && !p_style->b_bold;
1737 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1738 match = match && p_style->b_italic;
1740 match = match && !p_style->b_italic;
1748 FT_Done_Face( p_face );
1753 return VLC_EGENERIC;
1756 static int ProcessLines( filter_t *p_filter,
1761 uint32_t *pi_run_lengths,
1762 ft_style_t **pp_styles,
1763 line_desc_t **pp_lines,
1765 FT_Vector *p_result,
1769 uint32_t *pi_k_run_lengths,
1770 uint32_t *pi_k_durations )
1772 filter_sys_t *p_sys = p_filter->p_sys;
1773 ft_style_t **pp_char_styles;
1774 int *p_new_positions = NULL;
1775 int8_t *p_levels = NULL;
1776 uint8_t *pi_karaoke_bar = NULL;
1780 /* Assign each character in the text string its style explicitly, so that
1781 * after the characters have been shuffled around by Fribidi, we can re-apply
1782 * the styles, and to simplify the calculation of runs within a line.
1784 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1785 if( !pp_char_styles )
1790 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1791 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1792 * we just won't be able to display the progress bar; at least we'll
1798 for( j = 0; j < i_runs; j++ )
1799 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1800 pp_char_styles[ i++ ] = pp_styles[ j ];
1802 #if defined(HAVE_FRIBIDI)
1804 ft_style_t **pp_char_styles_new;
1805 int *p_old_positions;
1806 uint32_t *p_fribidi_string;
1807 int start_pos, pos = 0;
1809 pp_char_styles_new = (ft_style_t **)
1810 malloc( i_len * sizeof( ft_style_t * ));
1812 p_fribidi_string = (uint32_t *)
1813 malloc( (i_len + 1) * sizeof(uint32_t) );
1814 p_old_positions = (int *)
1815 malloc( (i_len + 1) * sizeof( int ) );
1816 p_new_positions = (int *)
1817 malloc( (i_len + 1) * sizeof( int ) );
1818 p_levels = (int8_t *)
1819 malloc( (i_len + 1) * sizeof( int8_t ) );
1821 if( ! pp_char_styles_new ||
1822 ! p_fribidi_string ||
1823 ! p_old_positions ||
1824 ! p_new_positions ||
1828 free( p_old_positions );
1829 free( p_new_positions );
1830 free( p_fribidi_string );
1831 free( pp_char_styles_new );
1832 free( pi_karaoke_bar );
1834 free( pp_char_styles );
1838 /* Do bidi conversion line-by-line */
1841 while(pos < i_len) {
1842 if (psz_text[pos] != '\n')
1844 p_fribidi_string[pos] = psz_text[pos];
1845 pp_char_styles_new[pos] = pp_char_styles[pos];
1846 p_new_positions[pos] = pos;
1851 while(pos < i_len) {
1852 if (psz_text[pos] == '\n')
1856 if (pos > start_pos)
1858 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1859 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1860 pos - start_pos, &base_dir,
1861 (FriBidiChar*)p_fribidi_string + start_pos,
1862 p_new_positions + start_pos,
1864 p_levels + start_pos );
1865 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1867 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1868 p_old_positions[ j - start_pos ] ];
1869 p_new_positions[ j ] += start_pos;
1873 free( p_old_positions );
1874 free( pp_char_styles );
1875 pp_char_styles = pp_char_styles_new;
1876 psz_text = p_fribidi_string;
1877 p_fribidi_string[ i_len ] = 0;
1880 /* Work out the karaoke */
1881 if( pi_karaoke_bar )
1883 int64_t i_last_duration = 0;
1884 int64_t i_duration = 0;
1885 int64_t i_start_pos = 0;
1886 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1888 for( k = 0; k< i_k_runs; k++ )
1890 double fraction = 0.0;
1892 i_duration += pi_k_durations[ k ];
1894 if( i_duration < i_elapsed )
1896 /* Completely finished this run-length -
1897 * let it render normally */
1901 else if( i_elapsed < i_last_duration )
1903 /* Haven't got up to this segment yet -
1904 * render it completely in karaoke BG mode */
1910 /* Partway through this run */
1912 fraction = (double)(i_elapsed - i_last_duration) /
1913 (double)pi_k_durations[ k ];
1915 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1917 double shade = pi_k_run_lengths[ k ] * fraction;
1919 if( p_new_positions )
1920 j = p_new_positions[ i_start_pos + i ];
1922 j = i_start_pos + i;
1924 if( i < (uint32_t)shade )
1925 pi_karaoke_bar[ j ] = 0xff;
1926 else if( (double)i > shade )
1927 pi_karaoke_bar[ j ] = 0x00;
1930 shade -= (int)shade;
1931 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1932 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1936 i_last_duration = i_duration;
1937 i_start_pos += pi_k_run_lengths[ k ];
1941 free( p_new_positions );
1943 FT_Vector tmp_result;
1945 line_desc_t *p_line = NULL;
1946 line_desc_t *p_prev = NULL;
1952 p_result->x = p_result->y = 0;
1953 tmp_result.x = tmp_result.y = 0;
1956 for( k = 0; k <= (uint32_t) i_len; k++ )
1958 if( ( k == (uint32_t) i_len ) ||
1960 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1962 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1964 /* End of the current style run */
1965 FT_Face p_face = NULL;
1968 /* Look for a match amongst our attachments first */
1969 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1973 char *psz_fontfile = NULL;
1975 psz_fontfile = FontConfig_Select( NULL,
1976 p_style->psz_fontname,
1980 if( psz_fontfile && ! *psz_fontfile )
1982 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1983 " so using default font", p_style->psz_fontname,
1984 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1985 (p_style->b_bold ? "(Bold)" :
1986 (p_style->b_italic ? "(Italic)" : ""))) );
1987 free( psz_fontfile );
1988 psz_fontfile = NULL;
1993 if( FT_New_Face( p_sys->p_library,
1994 psz_fontfile, i_idx, &p_face ) )
1996 free( psz_fontfile );
1997 free( pp_char_styles );
1998 #if defined(HAVE_FRIBIDI)
2001 free( pi_karaoke_bar );
2002 return VLC_EGENERIC;
2004 free( psz_fontfile );
2008 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2010 /* We've loaded a font face which is unhelpful for actually
2011 * rendering text - fallback to the default one.
2013 FT_Done_Face( p_face );
2017 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2018 ft_encoding_unicode ) ||
2019 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2020 p_style->i_font_size ) )
2022 if( p_face ) FT_Done_Face( p_face );
2023 free( pp_char_styles );
2024 #if defined(HAVE_FRIBIDI)
2027 free( pi_karaoke_bar );
2028 return VLC_EGENERIC;
2030 p_sys->i_use_kerning =
2031 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2034 uint32_t *psz_unicode = (uint32_t *)
2035 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2038 if( p_face ) FT_Done_Face( p_face );
2039 free( pp_char_styles );
2040 free( psz_unicode );
2041 #if defined(HAVE_FRIBIDI)
2044 free( pi_karaoke_bar );
2047 memcpy( psz_unicode, psz_text + i_prev,
2048 sizeof( uint32_t ) * ( k - i_prev ) );
2049 psz_unicode[ k - i_prev ] = 0;
2050 while( *psz_unicode )
2054 if( !(p_line = NewLine( i_len - i_prev)) )
2056 if( p_face ) FT_Done_Face( p_face );
2057 free( pp_char_styles );
2058 free( psz_unicode );
2059 #if defined(HAVE_FRIBIDI)
2062 free( pi_karaoke_bar );
2065 /* New Color mode only works in YUVA rendering mode --
2066 * (RGB mode has palette constraints on it). We therefore
2067 * need to populate the legacy colour fields also.
2069 p_line->b_new_color_mode = true;
2070 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2071 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2072 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2073 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2074 p_line->p_next = NULL;
2076 i_pen_y += tmp_result.y;
2080 if( p_prev ) p_prev->p_next = p_line;
2081 else *pp_lines = p_line;
2084 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2085 p_style->i_font_color, p_style->b_underline,
2089 p_style->i_karaoke_bg_color,
2090 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2091 &tmp_result ) != VLC_SUCCESS )
2093 if( p_face ) FT_Done_Face( p_face );
2094 free( pp_char_styles );
2095 free( psz_unicode );
2096 #if defined(HAVE_FRIBIDI)
2099 free( pi_karaoke_bar );
2100 return VLC_EGENERIC;
2105 p_result->x = __MAX( p_result->x, tmp_result.x );
2106 p_result->y += tmp_result.y;
2111 if( *psz_unicode == '\n')
2115 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2117 *c_ptr = *(c_ptr+1);
2122 free( psz_unicode );
2123 if( p_face ) FT_Done_Face( p_face );
2127 free( pp_char_styles );
2128 #if defined(HAVE_FRIBIDI)
2133 p_result->x = __MAX( p_result->x, tmp_result.x );
2134 p_result->y += tmp_result.y;
2137 if( pi_karaoke_bar )
2140 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2142 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2144 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2148 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2150 /* 100% BG colour will render faster if we
2151 * instead make it 100% FG colour, so leave
2152 * the ratio alone and copy the value across
2154 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2158 if( pi_karaoke_bar[ i ] & 0x80 )
2160 /* Swap Left and Right sides over for Right aligned
2161 * language text (eg. Arabic, Hebrew)
2163 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2165 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2166 p_line->p_bg_rgb[ k ] = i_tmp;
2168 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2171 /* Jump over the '\n' at the line-end */
2174 free( pi_karaoke_bar );
2180 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2181 subpicture_region_t *p_region_in )
2183 int rv = VLC_SUCCESS;
2184 stream_t *p_sub = NULL;
2185 xml_reader_t *p_xml_reader = NULL;
2187 if( !p_region_in || !p_region_in->psz_html )
2188 return VLC_EGENERIC;
2190 /* Reset the default fontsize in case screen metrics have changed */
2191 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2193 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2194 (uint8_t *) p_region_in->psz_html,
2195 strlen( p_region_in->psz_html ),
2199 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2200 if( p_filter->p_sys->p_xml )
2202 bool b_karaoke = false;
2204 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2207 /* Look for Root Node */
2208 if( xml_ReaderRead( p_xml_reader ) == 1 )
2210 char *psz_node = xml_ReaderName( p_xml_reader );
2212 if( !strcasecmp( "karaoke", psz_node ) )
2214 /* We're going to have to render the text a number
2215 * of times to show the progress marker on the text.
2217 var_SetBool( p_filter, "text-rerender", true );
2220 else if( !strcasecmp( "text", psz_node ) )
2226 /* Only text and karaoke tags are supported */
2227 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2228 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2229 p_xml_reader = NULL;
2241 uint32_t i_runs = 0;
2242 uint32_t i_k_runs = 0;
2243 uint32_t *pi_run_lengths = NULL;
2244 uint32_t *pi_k_run_lengths = NULL;
2245 uint32_t *pi_k_durations = NULL;
2246 ft_style_t **pp_styles = NULL;
2248 line_desc_t *p_lines = NULL;
2250 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2251 sizeof( uint32_t ) );
2256 rv = ProcessNodes( p_filter, p_xml_reader,
2257 p_region_in->p_style, psz_text, &i_len,
2258 &i_runs, &pi_run_lengths, &pp_styles,
2260 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2263 p_region_out->i_x = p_region_in->i_x;
2264 p_region_out->i_y = p_region_in->i_y;
2266 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2268 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2269 pi_run_lengths, pp_styles, &p_lines, &result,
2270 b_karaoke, i_k_runs, pi_k_run_lengths,
2274 for( k=0; k<i_runs; k++)
2275 DeleteStyle( pp_styles[k] );
2277 free( pi_run_lengths );
2280 /* Don't attempt to render text that couldn't be layed out
2283 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2285 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2287 Render( p_filter, p_region_out, p_lines,
2288 result.x, result.y );
2292 RenderYUVA( p_filter, p_region_out, p_lines,
2293 result.x, result.y );
2297 FreeLines( p_lines );
2299 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2302 stream_Delete( p_sub );
2308 static char* FontConfig_Select( FcConfig* priv, const char* family,
2309 bool b_bold, bool b_italic, int *i_idx )
2312 FcPattern *pat, *p_pat;
2316 pat = FcPatternCreate();
2317 if (!pat) return NULL;
2319 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2320 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2321 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2322 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2324 FcDefaultSubstitute( pat );
2326 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2328 FcPatternDestroy( pat );
2332 p_pat = FcFontMatch( priv, pat, &result );
2333 FcPatternDestroy( pat );
2334 if( !p_pat ) return NULL;
2336 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2337 || ( val_b != FcTrue ) )
2339 FcPatternDestroy( p_pat );
2342 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2347 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2349 FcPatternDestroy( p_pat );
2354 if( strcasecmp((const char*)val_s, family ) != 0 )
2355 msg_Warn( p_filter, "fontconfig: selected font family is not"
2356 "the requested one: '%s' != '%s'\n",
2357 (const char*)val_s, family );
2360 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2362 FcPatternDestroy( p_pat );
2366 FcPatternDestroy( p_pat );
2367 return strdup( (const char*)val_s );
2371 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2372 uint32_t **psz_text_out, uint32_t *pi_runs,
2373 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2374 ft_style_t *p_style )
2376 VLC_UNUSED(p_filter);
2377 VLC_UNUSED(psz_text_in);
2378 VLC_UNUSED(psz_text_out);
2379 VLC_UNUSED(pi_runs);
2380 VLC_UNUSED(ppi_run_lengths);
2381 VLC_UNUSED(ppp_styles);
2382 VLC_UNUSED(p_style);
2385 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2386 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2387 bool b_uline, bool b_through )
2390 VLC_UNUSED(p_fonts);
2392 VLC_UNUSED(b_italic);
2393 VLC_UNUSED(b_uline);
2394 VLC_UNUSED(b_through);
2399 static void FreeLine( line_desc_t *p_line )
2402 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2404 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2406 free( p_line->pp_glyphs );
2407 free( p_line->p_glyph_pos );
2408 free( p_line->p_fg_rgb );
2409 free( p_line->p_bg_rgb );
2410 free( p_line->p_fg_bg_ratio );
2411 free( p_line->pi_underline_offset );
2412 free( p_line->pi_underline_thickness );
2416 static void FreeLines( line_desc_t *p_lines )
2418 line_desc_t *p_line, *p_next;
2420 if( !p_lines ) return;
2422 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2424 p_next = p_line->p_next;
2429 static line_desc_t *NewLine( int i_count )
2431 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2433 if( !p_line ) return NULL;
2434 p_line->i_height = 0;
2435 p_line->i_width = 0;
2436 p_line->p_next = NULL;
2438 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2439 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2440 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2441 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2442 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2443 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2444 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2445 if( ( p_line->pp_glyphs == NULL ) ||
2446 ( p_line->p_glyph_pos == NULL ) ||
2447 ( p_line->p_fg_rgb == NULL ) ||
2448 ( p_line->p_bg_rgb == NULL ) ||
2449 ( p_line->p_fg_bg_ratio == NULL ) ||
2450 ( p_line->pi_underline_offset == NULL ) ||
2451 ( p_line->pi_underline_thickness == NULL ) )
2453 free( p_line->pi_underline_thickness );
2454 free( p_line->pi_underline_offset );
2455 free( p_line->p_fg_rgb );
2456 free( p_line->p_bg_rgb );
2457 free( p_line->p_fg_bg_ratio );
2458 free( p_line->p_glyph_pos );
2459 free( p_line->pp_glyphs );
2463 p_line->pp_glyphs[0] = NULL;
2464 p_line->b_new_color_mode = false;
2469 static int GetFontSize( filter_t *p_filter )
2471 filter_sys_t *p_sys = p_filter->p_sys;
2475 if( p_sys->i_default_font_size )
2477 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2478 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2480 i_size = p_sys->i_default_font_size;
2484 var_Get( p_filter, "freetype-rel-fontsize", &val );
2487 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2488 p_filter->p_sys->i_display_height =
2489 p_filter->fmt_out.video.i_height;
2494 msg_Warn( p_filter, "invalid fontsize, using 12" );
2495 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2496 i_size = 12 * val.i_int / 1000;
2503 static int SetFontSize( filter_t *p_filter, int i_size )
2505 filter_sys_t *p_sys = p_filter->p_sys;
2509 i_size = GetFontSize( p_filter );
2511 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2514 p_sys->i_font_size = i_size;
2516 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2518 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2519 return VLC_EGENERIC;
2525 static void YUVFromRGB( uint32_t i_argb,
2526 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2528 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2529 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2530 int i_blue = ( i_argb & 0x000000ff );
2532 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2533 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2534 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2535 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2536 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2537 -585 * i_blue + 4096 + 1048576) >> 13, 240);