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>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
42 #include <vlc_dialog.h>
43 #include <vlc_memory.h>
48 #include <freetype/ftsynth.h>
49 #include FT_FREETYPE_H
51 #define FT_FLOOR(X) ((X & -64) >> 6)
52 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
54 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
59 #define FC_DEFAULT_FONT "Arial Black"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
66 #elif defined( HAVE_MAEMO )
67 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
68 #define FC_DEFAULT_FONT "Nokia Sans Bold"
70 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
71 #define FC_DEFAULT_FONT "Serif Bold"
74 #if defined(HAVE_FRIBIDI)
75 #include <fribidi/fribidi.h>
78 #ifdef HAVE_FONTCONFIG
79 #include <fontconfig/fontconfig.h>
81 #define DEFAULT_FONT FC_DEFAULT_FONT
86 /*****************************************************************************
88 *****************************************************************************/
89 static int Create ( vlc_object_t * );
90 static void Destroy( vlc_object_t * );
92 #define FONT_TEXT N_("Font")
94 #ifdef HAVE_FONTCONFIG
95 #define FONT_LONGTEXT N_("Font family for the font you want to use")
97 #define FONT_LONGTEXT N_("Font file for the font you want to use")
100 #define FONTSIZE_TEXT N_("Font size in pixels")
101 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
102 "that will be rendered on the video. " \
103 "If set to something different than 0 this option will override the " \
104 "relative font size." )
105 #define OPACITY_TEXT N_("Opacity")
106 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
107 "text that will be rendered on the video. 0 = transparent, " \
108 "255 = totally opaque. " )
109 #define COLOR_TEXT N_("Text default color")
110 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
111 "the video. This must be an hexadecimal (like HTML colors). The first two "\
112 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
113 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
114 #define FONTSIZER_TEXT N_("Relative font size")
115 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
116 "fonts that will be rendered on the video. If absolute font size is set, "\
117 "relative size will be overridden." )
119 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
120 static const char *const ppsz_sizes_text[] = {
121 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
122 #define YUVP_TEXT N_("Use YUVP renderer")
123 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
124 "This option is only needed if you want to encode into DVB subtitles" )
125 #define EFFECT_TEXT N_("Font Effect")
126 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
127 "text to improve its readability." )
129 #define EFFECT_BACKGROUND 1
130 #define EFFECT_OUTLINE 2
131 #define EFFECT_OUTLINE_FAT 3
133 static int const pi_effects[] = { 1, 2, 3 };
134 static const char *const ppsz_effects_text[] = {
135 N_("Background"),N_("Outline"), N_("Fat Outline") };
136 static const int pi_color_values[] = {
137 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
138 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
139 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
141 static const char *const ppsz_color_descriptions[] = {
142 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
143 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
144 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
147 set_shortname( N_("Text renderer"))
148 set_description( N_("Freetype2 font renderer") )
149 set_category( CAT_VIDEO )
150 set_subcategory( SUBCAT_VIDEO_SUBPIC )
152 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
155 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
156 FONTSIZE_LONGTEXT, true )
158 /* opacity valid on 0..255, with default 255 = fully opaque */
159 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
160 OPACITY_TEXT, OPACITY_LONGTEXT, true )
162 /* hook to the color values list, with default 0x00ffffff = white */
163 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
164 COLOR_LONGTEXT, false )
165 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
167 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
168 FONTSIZER_LONGTEXT, false )
169 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
170 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
171 EFFECT_LONGTEXT, false )
172 change_integer_list( pi_effects, ppsz_effects_text, NULL )
174 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
175 YUVP_LONGTEXT, true )
176 set_capability( "text renderer", 100 )
177 add_shortcut( "text" )
178 set_callbacks( Create, Destroy )
183 /*****************************************************************************
185 *****************************************************************************/
187 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
188 static int RenderText( filter_t *, subpicture_region_t *,
189 subpicture_region_t * );
190 #ifdef HAVE_FONTCONFIG
191 static int RenderHtml( filter_t *, subpicture_region_t *,
192 subpicture_region_t * );
193 static char *FontConfig_Select( FcConfig *, const char *,
198 static int LoadFontsFromAttachments( filter_t *p_filter );
200 static int GetFontSize( filter_t *p_filter );
201 static int SetFontSize( filter_t *, int );
202 static void YUVFromRGB( uint32_t i_argb,
203 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
205 typedef struct line_desc_t line_desc_t;
208 /** NULL-terminated list of glyphs making the string */
209 FT_BitmapGlyph *pp_glyphs;
210 /** list of relative positions for the glyphs */
211 FT_Vector *p_glyph_pos;
212 /** list of RGB information for styled text
213 * -- if the rendering mode supports it (RenderYUVA) and
214 * b_new_color_mode is set, then it becomes possible to
215 * have multicoloured text within the subtitles. */
218 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
219 bool b_new_color_mode;
220 /** underline information -- only supplied if text should be underlined */
221 int *pi_underline_offset;
222 uint16_t *pi_underline_thickness;
226 int i_red, i_green, i_blue;
231 static line_desc_t *NewLine( int );
236 uint32_t i_font_color; /* ARGB */
237 uint32_t i_karaoke_bg_color; /* ARGB */
245 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
246 static void FreeLines( line_desc_t * );
247 static void FreeLine( line_desc_t * );
249 /*****************************************************************************
250 * filter_sys_t: freetype local data
251 *****************************************************************************
252 * This structure is part of the video output thread descriptor.
253 * It describes the freetype specific properties of an output thread.
254 *****************************************************************************/
257 FT_Library p_library; /* handle to library */
258 FT_Face p_face; /* handle to face object */
260 uint8_t i_font_opacity;
265 int i_default_font_size;
266 int i_display_height;
267 #ifdef HAVE_FONTCONFIG
268 char* psz_fontfamily;
272 input_attachment_t **pp_font_attachments;
273 int i_font_attachments;
277 #define UCHAR uint32_t
278 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
279 #define TR_FONT_STYLE_PTR ft_style_t *
281 #include "text_renderer.h"
283 /*****************************************************************************
284 * Create: allocates osd-text video thread output method
285 *****************************************************************************
286 * This function allocates and initializes a Clone vout method.
287 *****************************************************************************/
288 static int Create( vlc_object_t *p_this )
290 filter_t *p_filter = (filter_t *)p_this;
292 char *psz_fontfile=NULL;
293 char *psz_fontfamily=NULL;
294 int i_error,fontindex;
296 #ifdef HAVE_FONTCONFIG
297 FcPattern *fontpattern = NULL, *fontmatch = NULL;
298 /* Initialise result to Match, as fontconfig doesnt
299 * really set this other than some error-cases */
300 FcResult fontresult = FcResultMatch;
304 /* Allocate structure */
305 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
308 #ifdef HAVE_FONTCONFIG
309 p_sys->psz_fontfamily = NULL;
313 p_sys->p_library = 0;
314 p_sys->i_font_size = 0;
315 p_sys->i_display_height = 0;
317 var_Create( p_filter, "freetype-rel-fontsize",
318 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
320 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
321 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
322 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
323 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
324 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
325 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
326 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
329 if( !psz_fontfamily || !*psz_fontfamily )
331 free( psz_fontfamily );
332 #ifdef HAVE_FONTCONFIG
333 psz_fontfamily=strdup( DEFAULT_FONT );
335 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
336 if( !psz_fontfamily )
339 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
340 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
342 strcpy( psz_fontfamily, DEFAULT_FONT );
344 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
348 #ifdef HAVE_FONTCONFIG
349 msg_Dbg( p_filter, "Building font databases.");
354 dialog_progress_bar_t *p_dialog = NULL;
355 FcConfig *fcConfig = FcInitLoadConfig();
357 p_dialog = dialog_ProgressCreate( p_filter,
358 _("Building font cache"),
359 _("Please wait while your font cache is rebuilt.\n"
360 "This should take less than a few minutes."), NULL );
363 dialog_ProgressSet( p_dialog, NULL, 0.5 );
365 FcConfigBuildFonts( fcConfig );
367 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
371 dialog_ProgressSet( p_dialog, NULL, 1.0 );
372 dialog_ProgressDestroy( p_dialog );
376 /* Lets find some fontfile from freetype-font variable family */
378 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
381 fontpattern = FcPatternCreate();
384 msg_Err( p_filter, "Creating fontpattern failed");
388 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
389 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
390 free( psz_fontsize );
392 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
394 msg_Err( p_filter, "FontSubstitute failed");
397 FcDefaultSubstitute( fontpattern );
399 /* testing fontresult here doesn't do any good really, but maybe it will
400 * in future as fontconfig code doesn't set it in all cases and just
401 * returns NULL or doesn't set to to Match on all Match cases.*/
402 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
403 if( !fontmatch || fontresult == FcResultNoMatch )
405 msg_Err( p_filter, "Fontmatching failed");
409 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
410 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
413 msg_Err( p_filter, "Failed to get fontfile");
417 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
418 psz_fontfile ? psz_fontfile : "(null)" );
419 p_sys->psz_fontfamily = strdup( psz_fontfamily );
423 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
424 psz_fontfile = psz_fontfamily;
428 i_error = FT_Init_FreeType( &p_sys->p_library );
431 msg_Err( p_filter, "couldn't initialize freetype" );
435 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
436 fontindex, &p_sys->p_face );
438 if( i_error == FT_Err_Unknown_File_Format )
440 msg_Err( p_filter, "file %s have unknown format",
441 psz_fontfile ? psz_fontfile : "(null)" );
446 msg_Err( p_filter, "failed to load font file %s",
447 psz_fontfile ? psz_fontfile : "(null)" );
451 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
454 msg_Err( p_filter, "font has no unicode translation table" );
458 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
460 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
463 p_sys->pp_font_attachments = NULL;
464 p_sys->i_font_attachments = 0;
466 p_filter->pf_render_text = RenderText;
467 #ifdef HAVE_FONTCONFIG
468 p_filter->pf_render_html = RenderHtml;
469 FcPatternDestroy( fontmatch );
470 FcPatternDestroy( fontpattern );
472 p_filter->pf_render_html = NULL;
475 free( psz_fontfamily );
476 LoadFontsFromAttachments( p_filter );
481 #ifdef HAVE_FONTCONFIG
482 if( fontmatch ) FcPatternDestroy( fontmatch );
483 if( fontpattern ) FcPatternDestroy( fontpattern );
488 dialog_ProgressDestroy( p_dialog );
491 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
492 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
493 free( psz_fontfamily );
498 /*****************************************************************************
499 * Destroy: destroy Clone video thread output method
500 *****************************************************************************
501 * Clean up all data and library connections
502 *****************************************************************************/
503 static void Destroy( vlc_object_t *p_this )
505 filter_t *p_filter = (filter_t *)p_this;
506 filter_sys_t *p_sys = p_filter->p_sys;
508 if( p_sys->pp_font_attachments )
512 for( k = 0; k < p_sys->i_font_attachments; k++ )
513 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
515 free( p_sys->pp_font_attachments );
518 #ifdef HAVE_FONTCONFIG
519 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
520 free( p_sys->psz_fontfamily );
523 /* FcFini asserts calling the subfunction FcCacheFini()
524 * even if no other library functions have been made since FcInit(),
525 * so don't call it. */
527 FT_Done_Face( p_sys->p_face );
528 FT_Done_FreeType( p_sys->p_library );
532 /*****************************************************************************
533 * Make any TTF/OTF fonts present in the attachments of the media file
534 * and store them for later use by the FreeType Engine
535 *****************************************************************************/
536 static int LoadFontsFromAttachments( filter_t *p_filter )
538 filter_sys_t *p_sys = p_filter->p_sys;
539 input_thread_t *p_input;
540 input_attachment_t **pp_attachments;
541 int i_attachments_cnt;
543 int rv = VLC_SUCCESS;
545 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
549 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
551 vlc_object_release(p_input);
555 p_sys->i_font_attachments = 0;
556 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
557 if(! p_sys->pp_font_attachments )
560 for( k = 0; k < i_attachments_cnt; k++ )
562 input_attachment_t *p_attach = pp_attachments[k];
564 if( p_sys->pp_font_attachments )
566 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
567 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
568 ( p_attach->i_data > 0 ) &&
569 ( p_attach->p_data != NULL ) )
571 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
575 vlc_input_attachment_Delete( p_attach );
580 vlc_input_attachment_Delete( p_attach );
583 free( pp_attachments );
585 vlc_object_release(p_input);
590 /*****************************************************************************
591 * Render: place string in picture
592 *****************************************************************************
593 * This function merges the previously rendered freetype glyphs into a picture
594 *****************************************************************************/
595 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
596 line_desc_t *p_line, int i_width, int i_height )
598 VLC_UNUSED(p_filter);
599 static const uint8_t pi_gamma[16] =
600 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
601 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
605 int i, x, y, i_pitch;
606 uint8_t i_y; /* YUV values, derived from incoming RGB */
609 /* Create a new subpicture region */
610 memset( &fmt, 0, sizeof(video_format_t) );
611 fmt.i_chroma = VLC_CODEC_YUVP;
612 fmt.i_width = fmt.i_visible_width = i_width + 4;
613 fmt.i_height = fmt.i_visible_height = i_height + 4;
614 if( p_region->fmt.i_visible_width > 0 )
615 fmt.i_visible_width = p_region->fmt.i_visible_width;
616 if( p_region->fmt.i_visible_height > 0 )
617 fmt.i_visible_height = p_region->fmt.i_visible_height;
618 fmt.i_x_offset = fmt.i_y_offset = 0;
620 assert( !p_region->p_picture );
621 p_region->p_picture = picture_NewFromFormat( &fmt );
622 if( !p_region->p_picture )
624 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
627 /* Calculate text color components */
628 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
629 25 * p_line->i_blue + 128) >> 8) + 16;
630 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
631 112 * p_line->i_blue + 128) >> 8) + 128;
632 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
633 18 * p_line->i_blue + 128) >> 8) + 128;
636 fmt.p_palette->i_entries = 16;
637 for( i = 0; i < 8; i++ )
639 fmt.p_palette->palette[i][0] = 0;
640 fmt.p_palette->palette[i][1] = 0x80;
641 fmt.p_palette->palette[i][2] = 0x80;
642 fmt.p_palette->palette[i][3] = pi_gamma[i];
643 fmt.p_palette->palette[i][3] =
644 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
646 for( i = 8; i < fmt.p_palette->i_entries; i++ )
648 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
649 fmt.p_palette->palette[i][1] = i_u;
650 fmt.p_palette->palette[i][2] = i_v;
651 fmt.p_palette->palette[i][3] = pi_gamma[i];
652 fmt.p_palette->palette[i][3] =
653 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
656 p_dst = p_region->p_picture->Y_PIXELS;
657 i_pitch = p_region->p_picture->Y_PITCH;
659 /* Initialize the region pixels */
660 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
662 for( ; p_line != NULL; p_line = p_line->p_next )
664 int i_glyph_tmax = 0;
665 int i_bitmap_offset, i_offset, i_align_offset = 0;
666 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
668 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
669 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
672 if( p_line->i_width < i_width )
674 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
676 i_align_offset = i_width - p_line->i_width;
678 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
680 i_align_offset = ( i_width - p_line->i_width ) / 2;
684 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
686 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
688 i_offset = ( p_line->p_glyph_pos[ i ].y +
689 i_glyph_tmax - p_glyph->top + 2 ) *
690 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
693 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
695 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
697 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
699 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
706 /* Outlining (find something better than nearest neighbour filtering ?) */
709 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
710 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
711 uint8_t left, current;
713 for( y = 1; y < (int)fmt.i_height - 1; y++ )
715 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
716 p_dst += p_region->p_picture->Y_PITCH;
719 for( x = 1; x < (int)fmt.i_width - 1; x++ )
722 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
723 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;
727 memset( p_top, 0, fmt.i_width );
733 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
734 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
735 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
736 int i_glyph_tmax, int i_align_offset,
737 uint8_t i_y, uint8_t i_u, uint8_t i_v,
738 subpicture_region_t *p_region)
742 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
744 p_dst_y = p_region->p_picture->Y_PIXELS;
745 p_dst_u = p_region->p_picture->U_PIXELS;
746 p_dst_v = p_region->p_picture->V_PIXELS;
747 p_dst_a = p_region->p_picture->A_PIXELS;
748 i_pitch = p_region->p_picture->A_PITCH;
750 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
751 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
753 for( y = 0; y < i_line_thickness; y++ )
755 int i_extra = p_this_glyph->bitmap.width;
759 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
760 (p_this_glyph_pos->x + p_this_glyph->left);
762 for( x = 0; x < i_extra; x++ )
766 /* break the underline around the tails of any glyphs which cross it */
767 /* Strikethrough doesn't get broken */
768 for( z = x - i_line_thickness;
769 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
772 if( p_next_glyph && ( z >= i_extra ) )
774 int i_row = i_line_offset + p_next_glyph->top + y;
776 if( ( p_next_glyph->bitmap.rows > i_row ) &&
777 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
782 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
784 int i_row = i_line_offset + p_this_glyph->top + y;
786 if( ( p_this_glyph->bitmap.rows > i_row ) &&
787 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
796 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
797 p_dst_u[i_offset+x] = i_u;
798 p_dst_v[i_offset+x] = i_v;
799 p_dst_a[i_offset+x] = 255;
806 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
808 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
809 int i_pitch = p_region->p_picture->A_PITCH;
812 for( ; p_line != NULL; p_line = p_line->p_next )
814 int i_glyph_tmax=0, i = 0;
815 int i_bitmap_offset, i_offset, i_align_offset = 0;
816 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
818 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
819 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
822 if( p_line->i_width < i_width )
824 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
826 i_align_offset = i_width - p_line->i_width;
828 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
830 i_align_offset = ( i_width - p_line->i_width ) / 2;
834 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
836 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
838 i_offset = ( p_line->p_glyph_pos[ i ].y +
839 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
840 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
841 i_align_offset +xoffset;
843 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
845 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
847 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
848 if( p_dst[i_offset+x] <
849 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
851 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
860 /*****************************************************************************
861 * Render: place string in picture
862 *****************************************************************************
863 * This function merges the previously rendered freetype glyphs into a picture
864 *****************************************************************************/
865 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
866 line_desc_t *p_line, int i_width, int i_height )
868 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
870 int i, x, y, i_pitch, i_alpha;
871 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
873 if( i_width == 0 || i_height == 0 )
876 /* Create a new subpicture region */
877 memset( &fmt, 0, sizeof(video_format_t) );
878 fmt.i_chroma = VLC_CODEC_YUVA;
879 fmt.i_width = fmt.i_visible_width = i_width + 6;
880 fmt.i_height = fmt.i_visible_height = i_height + 6;
881 if( p_region->fmt.i_visible_width > 0 )
882 fmt.i_visible_width = p_region->fmt.i_visible_width;
883 if( p_region->fmt.i_visible_height > 0 )
884 fmt.i_visible_height = p_region->fmt.i_visible_height;
885 fmt.i_x_offset = fmt.i_y_offset = 0;
887 p_region->p_picture = picture_NewFromFormat( &fmt );
888 if( !p_region->p_picture )
892 /* Calculate text color components */
893 YUVFromRGB( (p_line->i_red << 16) |
894 (p_line->i_green << 8) |
897 i_alpha = p_line->i_alpha;
899 p_dst_y = p_region->p_picture->Y_PIXELS;
900 p_dst_u = p_region->p_picture->U_PIXELS;
901 p_dst_v = p_region->p_picture->V_PIXELS;
902 p_dst_a = p_region->p_picture->A_PIXELS;
903 i_pitch = p_region->p_picture->A_PITCH;
905 /* Initialize the region pixels */
906 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
908 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
909 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
910 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
911 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
915 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
916 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
917 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
918 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
920 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
921 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
923 DrawBlack( p_line, i_width, p_region, 0, 0);
924 DrawBlack( p_line, i_width, p_region, -1, 0);
925 DrawBlack( p_line, i_width, p_region, 0, -1);
926 DrawBlack( p_line, i_width, p_region, 1, 0);
927 DrawBlack( p_line, i_width, p_region, 0, 1);
930 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
932 DrawBlack( p_line, i_width, p_region, -1, -1);
933 DrawBlack( p_line, i_width, p_region, -1, 1);
934 DrawBlack( p_line, i_width, p_region, 1, -1);
935 DrawBlack( p_line, i_width, p_region, 1, 1);
937 DrawBlack( p_line, i_width, p_region, -2, 0);
938 DrawBlack( p_line, i_width, p_region, 0, -2);
939 DrawBlack( p_line, i_width, p_region, 2, 0);
940 DrawBlack( p_line, i_width, p_region, 0, 2);
942 DrawBlack( p_line, i_width, p_region, -2, -2);
943 DrawBlack( p_line, i_width, p_region, -2, 2);
944 DrawBlack( p_line, i_width, p_region, 2, -2);
945 DrawBlack( p_line, i_width, p_region, 2, 2);
947 DrawBlack( p_line, i_width, p_region, -3, 0);
948 DrawBlack( p_line, i_width, p_region, 0, -3);
949 DrawBlack( p_line, i_width, p_region, 3, 0);
950 DrawBlack( p_line, i_width, p_region, 0, 3);
953 for( ; p_line != NULL; p_line = p_line->p_next )
955 int i_glyph_tmax = 0;
956 int i_bitmap_offset, i_offset, i_align_offset = 0;
957 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
959 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
960 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
963 if( p_line->i_width < i_width )
965 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
967 i_align_offset = i_width - p_line->i_width;
969 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
971 i_align_offset = ( i_width - p_line->i_width ) / 2;
975 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
977 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
979 i_offset = ( p_line->p_glyph_pos[ i ].y +
980 i_glyph_tmax - p_glyph->top + 3 ) *
981 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
984 if( p_line->b_new_color_mode )
986 /* Every glyph can (and in fact must) have its own color */
987 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
990 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
992 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
994 uint8_t i_y_local = i_y;
995 uint8_t i_u_local = i_u;
996 uint8_t i_v_local = i_v;
998 if( p_line->p_fg_bg_ratio != 0x00 )
1000 int i_split = p_glyph->bitmap.width *
1001 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1005 YUVFromRGB( p_line->p_bg_rgb[ i ],
1006 &i_y_local, &i_u_local, &i_v_local );
1010 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1012 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1013 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1015 p_dst_u[i_offset+x] = i_u;
1016 p_dst_v[i_offset+x] = i_v;
1018 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1019 p_dst_a[i_offset+x] = 0xff;
1022 i_offset += i_pitch;
1025 if( p_line->pi_underline_thickness[ i ] )
1027 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1028 p_line->pi_underline_offset[ i ],
1029 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1030 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1031 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1032 i_glyph_tmax, i_align_offset,
1039 /* Apply the alpha setting */
1040 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1041 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1047 * This function renders a text subpicture region into another one.
1048 * It also calculates the size needed for this string, and renders the
1049 * needed glyphs into memory. It is used as pf_add_string callback in
1050 * the vout method by this module
1052 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1053 subpicture_region_t *p_region_in )
1055 filter_sys_t *p_sys = p_filter->p_sys;
1056 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1057 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1058 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1059 int i_string_length;
1061 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1062 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1072 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1073 psz_string = p_region_in->psz_text;
1074 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1076 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1077 i_scale = val.i_int;
1079 if( p_region_in->p_style )
1081 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1082 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1083 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1087 i_font_color = p_sys->i_font_color;
1088 i_font_alpha = 255 - p_sys->i_font_opacity;
1089 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1092 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1093 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1094 SetFontSize( p_filter, i_font_size );
1096 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1097 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1098 i_blue = i_font_color & 0x000000FF;
1100 result.x = result.y = 0;
1101 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1103 psz_unicode = psz_unicode_orig =
1104 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1105 if( psz_unicode == NULL )
1107 #if defined(WORDS_BIGENDIAN)
1108 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1110 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1112 if( iconv_handle == (vlc_iconv_t)-1 )
1114 msg_Warn( p_filter, "unable to do conversion" );
1120 const char *p_in_buffer = psz_string;
1121 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1122 i_in_bytes = strlen( psz_string );
1123 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1124 i_out_bytes_left = i_out_bytes;
1125 p_out_buffer = (char *)psz_unicode;
1126 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1128 &p_out_buffer, &i_out_bytes_left );
1130 vlc_iconv_close( iconv_handle );
1134 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1135 "bytes left %u", (unsigned)i_in_bytes );
1138 *(uint32_t*)p_out_buffer = 0;
1139 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1142 #if defined(HAVE_FRIBIDI)
1144 uint32_t *p_fribidi_string;
1145 int32_t start_pos, pos = 0;
1147 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1148 if( !p_fribidi_string )
1151 /* Do bidi conversion line-by-line */
1152 while( pos < i_string_length )
1154 while( pos < i_string_length )
1156 i_char = psz_unicode[pos];
1157 if (i_char != '\r' && i_char != '\n')
1159 p_fribidi_string[pos] = i_char;
1163 while( pos < i_string_length )
1165 i_char = psz_unicode[pos];
1166 if (i_char == '\r' || i_char == '\n')
1170 if (pos > start_pos)
1172 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1173 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1176 (FriBidiChar*)p_fribidi_string + start_pos,
1181 free( psz_unicode_orig );
1182 psz_unicode = psz_unicode_orig = p_fribidi_string;
1183 p_fribidi_string[ i_string_length ] = 0;
1187 /* Calculate relative glyph positions and a bounding box for the
1189 if( !(p_line = NewLine( strlen( psz_string ))) )
1192 i_pen_x = i_pen_y = 0;
1194 psz_line_start = psz_unicode;
1196 #define face p_sys->p_face
1197 #define glyph face->glyph
1199 while( *psz_unicode )
1201 i_char = *psz_unicode++;
1202 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1207 if( i_char == '\n' )
1209 psz_line_start = psz_unicode;
1210 if( !(p_next = NewLine( strlen( psz_string ))) )
1212 p_line->p_next = p_next;
1213 p_line->i_width = line.xMax;
1214 p_line->i_height = face->size->metrics.height >> 6;
1215 p_line->pp_glyphs[ i ] = NULL;
1216 p_line->i_alpha = i_font_alpha;
1217 p_line->i_red = i_red;
1218 p_line->i_green = i_green;
1219 p_line->i_blue = i_blue;
1222 result.x = __MAX( result.x, line.xMax );
1223 result.y += face->size->metrics.height >> 6;
1226 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1227 i_pen_y += face->size->metrics.height >> 6;
1229 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1234 i_glyph_index = FT_Get_Char_Index( face, i_char );
1235 if( p_sys->i_use_kerning && i_glyph_index
1239 FT_Get_Kerning( face, i_previous, i_glyph_index,
1240 ft_kerning_default, &delta );
1241 i_pen_x += delta.x >> 6;
1244 p_line->p_glyph_pos[ i ].x = i_pen_x;
1245 p_line->p_glyph_pos[ i ].y = i_pen_y;
1246 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1249 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1253 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1256 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1260 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1261 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1264 FT_Done_Glyph( tmp_glyph );
1267 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1270 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1271 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1272 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1274 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1275 p_line->pp_glyphs[ i ] = NULL;
1277 p_line = NewLine( strlen( psz_string ));
1278 if( p_prev ) p_prev->p_next = p_line;
1279 else p_lines = p_line;
1281 uint32_t *psz_unicode_saved = psz_unicode;
1282 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1286 if( psz_unicode == psz_line_start )
1287 { /* try harder to break that line */
1288 psz_unicode = psz_unicode_saved;
1289 while( psz_unicode > psz_line_start &&
1290 *psz_unicode != '_' && *psz_unicode != '/' &&
1291 *psz_unicode != '\\' && *psz_unicode != '.' )
1296 if( psz_unicode == psz_line_start )
1298 msg_Warn( p_filter, "unbreakable string" );
1303 *psz_unicode = '\n';
1305 psz_unicode = psz_line_start;
1308 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1311 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1312 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1314 i_previous = i_glyph_index;
1315 i_pen_x += glyph->advance.x >> 6;
1319 p_line->i_width = line.xMax;
1320 p_line->i_height = face->size->metrics.height >> 6;
1321 p_line->pp_glyphs[ i ] = NULL;
1322 p_line->i_alpha = i_font_alpha;
1323 p_line->i_red = i_red;
1324 p_line->i_green = i_green;
1325 p_line->i_blue = i_blue;
1326 result.x = __MAX( result.x, line.xMax );
1327 result.y += line.yMax - line.yMin;
1332 p_region_out->i_x = p_region_in->i_x;
1333 p_region_out->i_y = p_region_in->i_y;
1335 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1336 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1338 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1340 free( psz_unicode_orig );
1341 FreeLines( p_lines );
1345 free( psz_unicode_orig );
1346 FreeLines( p_lines );
1347 return VLC_EGENERIC;
1350 #ifdef HAVE_FONTCONFIG
1351 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1352 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1353 bool b_italic, bool b_uline, bool b_through )
1355 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1359 p_style->i_font_size = i_font_size;
1360 p_style->i_font_color = i_font_color;
1361 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1362 p_style->b_italic = b_italic;
1363 p_style->b_bold = b_bold;
1364 p_style->b_underline = b_uline;
1365 p_style->b_through = b_through;
1367 p_style->psz_fontname = strdup( psz_fontname );
1372 static void DeleteStyle( ft_style_t *p_style )
1376 free( p_style->psz_fontname );
1381 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1388 if(( s1->i_font_size == s2->i_font_size ) &&
1389 ( s1->i_font_color == s2->i_font_color ) &&
1390 ( s1->b_italic == s2->b_italic ) &&
1391 ( s1->b_through == s2->b_through ) &&
1392 ( s1->b_bold == s2->b_bold ) &&
1393 ( s1->b_underline == s2->b_underline ) &&
1394 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1401 static void IconvText( filter_t *p_filter, const char *psz_string,
1402 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1404 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1406 /* If memory hasn't been allocated for our output string, allocate it here
1407 * - the calling function must now be responsible for freeing it.
1409 if( !*ppsz_unicode )
1410 *ppsz_unicode = (uint32_t *)
1411 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1413 /* We don't need to handle a NULL pointer in *ppsz_unicode
1414 * if we are instead testing for a non NULL value like we are here */
1418 #if defined(WORDS_BIGENDIAN)
1419 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1421 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1423 if( iconv_handle != (vlc_iconv_t)-1 )
1425 char *p_in_buffer, *p_out_buffer;
1426 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1427 i_in_bytes = strlen( psz_string );
1428 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1429 i_out_bytes_left = i_out_bytes;
1430 p_in_buffer = (char *) psz_string;
1431 p_out_buffer = (char *) *ppsz_unicode;
1432 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1433 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1435 vlc_iconv_close( iconv_handle );
1439 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1440 "bytes left %u", (unsigned)i_in_bytes );
1444 *(uint32_t*)p_out_buffer = 0;
1446 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1451 msg_Warn( p_filter, "unable to do conversion" );
1456 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1457 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1458 bool b_uline, bool b_through )
1460 ft_style_t *p_style = NULL;
1462 char *psz_fontname = NULL;
1463 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1464 uint32_t i_karaoke_bg_color = i_font_color;
1465 int i_font_size = p_sys->i_font_size;
1467 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1468 &i_font_color, &i_karaoke_bg_color ))
1470 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1471 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1476 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1477 bool b_uline, bool b_through, bool b_bold,
1478 bool b_italic, int i_karaoke_bgcolor,
1479 line_desc_t *p_line, uint32_t *psz_unicode,
1480 int *pi_pen_x, int i_pen_y, int *pi_start,
1481 FT_Vector *p_result )
1486 bool b_first_on_line = true;
1489 int i_pen_x_start = *pi_pen_x;
1491 uint32_t *psz_unicode_start = psz_unicode;
1493 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1495 /* Account for part of line already in position */
1496 for( i=0; i<*pi_start; i++ )
1500 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1501 ft_glyph_bbox_pixels, &glyph_size );
1503 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1504 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1505 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1506 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1512 b_first_on_line = false;
1514 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1520 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1521 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1525 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1526 ft_kerning_default, &delta );
1527 *pi_pen_x += delta.x >> 6;
1529 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1530 p_line->p_glyph_pos[ i ].y = i_pen_y;
1532 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1536 "unable to render text FT_Load_Glyph returned %d", i_error );
1537 p_line->pp_glyphs[ i ] = NULL;
1538 return VLC_EGENERIC;
1541 /* Do synthetic styling now that Freetype supports it;
1542 * ie. if the font we have loaded is NOT already in the
1543 * style that the tags want, then switch it on; if they
1544 * are then don't. */
1545 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1546 FT_GlyphSlot_Embolden( p_face->glyph );
1547 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1548 FT_GlyphSlot_Oblique( p_face->glyph );
1550 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1554 "unable to render text FT_Get_Glyph returned %d", i_error );
1555 p_line->pp_glyphs[ i ] = NULL;
1556 return VLC_EGENERIC;
1558 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1559 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1562 FT_Done_Glyph( tmp_glyph );
1565 if( b_uline || b_through )
1567 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1568 p_face->size->metrics.y_scale));
1569 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1570 p_face->size->metrics.y_scale));
1572 p_line->pi_underline_offset[ i ] =
1573 ( aOffset < 0 ) ? -aOffset : aOffset;
1574 p_line->pi_underline_thickness[ i ] =
1575 ( aSize < 0 ) ? -aSize : aSize;
1578 /* Move the baseline to make it strikethrough instead of
1579 * underline. That means that strikethrough takes precedence
1581 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1582 p_face->size->metrics.y_scale));
1584 p_line->pi_underline_offset[ i ] -=
1585 ( aDescent < 0 ) ? -aDescent : aDescent;
1589 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1590 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1591 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1592 p_line->p_fg_bg_ratio[ i ] = 0x00;
1594 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1595 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1596 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1598 for( ; i >= *pi_start; i-- )
1599 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1602 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1606 if( psz_unicode == psz_unicode_start )
1608 if( b_first_on_line )
1610 msg_Warn( p_filter, "unbreakable string" );
1611 p_line->pp_glyphs[ i ] = NULL;
1612 return VLC_EGENERIC;
1614 *pi_pen_x = i_pen_x_start;
1616 p_line->i_width = line.xMax;
1617 p_line->i_height = __MAX( p_line->i_height,
1618 p_face->size->metrics.height >> 6 );
1619 p_line->pp_glyphs[ i ] = NULL;
1621 p_result->x = __MAX( p_result->x, line.xMax );
1622 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1623 i_yMax - i_yMin ) );
1628 *psz_unicode = '\n';
1630 psz_unicode = psz_unicode_start;
1631 *pi_pen_x = i_pen_x_start;
1639 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1640 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1642 i_previous = i_glyph_index;
1643 *pi_pen_x += p_face->glyph->advance.x >> 6;
1646 p_line->i_width = line.xMax;
1647 p_line->i_height = __MAX( p_line->i_height,
1648 p_face->size->metrics.height >> 6 );
1649 p_line->pp_glyphs[ i ] = NULL;
1651 p_result->x = __MAX( p_result->x, line.xMax );
1652 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1653 line.yMax - line.yMin ) );
1657 /* Get rid of any text processed - if necessary repositioning
1658 * at the start of a new line of text
1662 *psz_unicode_start = '\0';
1664 else if( psz_unicode > psz_unicode_start )
1666 for( i=0; psz_unicode[ i ]; i++ )
1667 psz_unicode_start[ i ] = psz_unicode[ i ];
1668 psz_unicode_start[ i ] = '\0';
1674 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1675 uint32_t **psz_text_out, uint32_t *pi_runs,
1676 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1677 ft_style_t *p_style )
1679 uint32_t i_string_length = 0;
1681 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1682 *psz_text_out += i_string_length;
1684 if( ppp_styles && ppi_run_lengths )
1688 /* XXX this logic looks somewhat broken */
1692 *ppp_styles = realloc_or_free( *ppp_styles,
1693 *pi_runs * sizeof( ft_style_t * ) );
1695 else if( *pi_runs == 1 )
1697 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1700 /* We have just malloc'ed this memory successfully -
1701 * *pi_runs HAS to be within the memory area of *ppp_styles */
1704 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1708 /* XXX more iffy logic */
1710 if( *ppi_run_lengths )
1712 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1713 *pi_runs * sizeof( uint32_t ) );
1715 else if( *pi_runs == 1 )
1717 *ppi_run_lengths = (uint32_t *)
1718 malloc( *pi_runs * sizeof( uint32_t ) );
1721 /* same remarks here */
1722 if( *ppi_run_lengths )
1724 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1727 /* If we couldn't use the p_style argument due to memory allocation
1728 * problems above, release it here.
1730 if( p_style ) DeleteStyle( p_style );
1733 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1737 for( k=0; k < p_sys->i_font_attachments; k++ )
1739 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1741 FT_Face p_face = NULL;
1743 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1751 bool match = !strcasecmp( p_face->family_name,
1752 p_style->psz_fontname );
1754 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1755 match = match && p_style->b_bold;
1757 match = match && !p_style->b_bold;
1759 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1760 match = match && p_style->b_italic;
1762 match = match && !p_style->b_italic;
1770 FT_Done_Face( p_face );
1775 return VLC_EGENERIC;
1778 static int ProcessLines( filter_t *p_filter,
1783 uint32_t *pi_run_lengths,
1784 ft_style_t **pp_styles,
1785 line_desc_t **pp_lines,
1787 FT_Vector *p_result,
1791 uint32_t *pi_k_run_lengths,
1792 uint32_t *pi_k_durations )
1794 filter_sys_t *p_sys = p_filter->p_sys;
1795 ft_style_t **pp_char_styles;
1796 int *p_new_positions = NULL;
1797 int8_t *p_levels = NULL;
1798 uint8_t *pi_karaoke_bar = NULL;
1802 /* Assign each character in the text string its style explicitly, so that
1803 * after the characters have been shuffled around by Fribidi, we can re-apply
1804 * the styles, and to simplify the calculation of runs within a line.
1806 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1807 if( !pp_char_styles )
1812 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1813 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1814 * we just won't be able to display the progress bar; at least we'll
1820 for( j = 0; j < i_runs; j++ )
1821 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1822 pp_char_styles[ i++ ] = pp_styles[ j ];
1824 #if defined(HAVE_FRIBIDI)
1826 ft_style_t **pp_char_styles_new;
1827 int *p_old_positions;
1828 uint32_t *p_fribidi_string;
1829 int start_pos, pos = 0;
1831 pp_char_styles_new = (ft_style_t **)
1832 malloc( i_len * sizeof( ft_style_t * ));
1834 p_fribidi_string = (uint32_t *)
1835 malloc( (i_len + 1) * sizeof(uint32_t) );
1836 p_old_positions = (int *)
1837 malloc( (i_len + 1) * sizeof( int ) );
1838 p_new_positions = (int *)
1839 malloc( (i_len + 1) * sizeof( int ) );
1840 p_levels = (int8_t *)
1841 malloc( (i_len + 1) * sizeof( int8_t ) );
1843 if( ! pp_char_styles_new ||
1844 ! p_fribidi_string ||
1845 ! p_old_positions ||
1846 ! p_new_positions ||
1850 free( p_old_positions );
1851 free( p_new_positions );
1852 free( p_fribidi_string );
1853 free( pp_char_styles_new );
1854 free( pi_karaoke_bar );
1856 free( pp_char_styles );
1860 /* Do bidi conversion line-by-line */
1863 while(pos < i_len) {
1864 if (psz_text[pos] != '\n')
1866 p_fribidi_string[pos] = psz_text[pos];
1867 pp_char_styles_new[pos] = pp_char_styles[pos];
1868 p_new_positions[pos] = pos;
1873 while(pos < i_len) {
1874 if (psz_text[pos] == '\n')
1878 if (pos > start_pos)
1880 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1881 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1882 pos - start_pos, &base_dir,
1883 (FriBidiChar*)p_fribidi_string + start_pos,
1884 p_new_positions + start_pos,
1886 p_levels + start_pos );
1887 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1889 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1890 p_old_positions[ j - start_pos ] ];
1891 p_new_positions[ j ] += start_pos;
1895 free( p_old_positions );
1896 free( pp_char_styles );
1897 pp_char_styles = pp_char_styles_new;
1898 psz_text = p_fribidi_string;
1899 p_fribidi_string[ i_len ] = 0;
1902 /* Work out the karaoke */
1903 if( pi_karaoke_bar )
1905 int64_t i_last_duration = 0;
1906 int64_t i_duration = 0;
1907 int64_t i_start_pos = 0;
1908 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1910 for( k = 0; k< i_k_runs; k++ )
1912 double fraction = 0.0;
1914 i_duration += pi_k_durations[ k ];
1916 if( i_duration < i_elapsed )
1918 /* Completely finished this run-length -
1919 * let it render normally */
1923 else if( i_elapsed < i_last_duration )
1925 /* Haven't got up to this segment yet -
1926 * render it completely in karaoke BG mode */
1932 /* Partway through this run */
1934 fraction = (double)(i_elapsed - i_last_duration) /
1935 (double)pi_k_durations[ k ];
1937 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1939 double shade = pi_k_run_lengths[ k ] * fraction;
1941 if( p_new_positions )
1942 j = p_new_positions[ i_start_pos + i ];
1944 j = i_start_pos + i;
1946 if( i < (uint32_t)shade )
1947 pi_karaoke_bar[ j ] = 0xff;
1948 else if( (double)i > shade )
1949 pi_karaoke_bar[ j ] = 0x00;
1952 shade -= (int)shade;
1953 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1954 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1958 i_last_duration = i_duration;
1959 i_start_pos += pi_k_run_lengths[ k ];
1963 free( p_new_positions );
1965 FT_Vector tmp_result;
1967 line_desc_t *p_line = NULL;
1968 line_desc_t *p_prev = NULL;
1974 p_result->x = p_result->y = 0;
1975 tmp_result.x = tmp_result.y = 0;
1978 for( k = 0; k <= (uint32_t) i_len; k++ )
1980 if( ( k == (uint32_t) i_len ) ||
1982 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1984 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1986 /* End of the current style run */
1987 FT_Face p_face = NULL;
1990 /* Look for a match amongst our attachments first */
1991 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1995 char *psz_fontfile = NULL;
1997 psz_fontfile = FontConfig_Select( NULL,
1998 p_style->psz_fontname,
2002 if( psz_fontfile && ! *psz_fontfile )
2004 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2005 " so using default font", p_style->psz_fontname,
2006 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2007 (p_style->b_bold ? "(Bold)" :
2008 (p_style->b_italic ? "(Italic)" : ""))) );
2009 free( psz_fontfile );
2010 psz_fontfile = NULL;
2015 if( FT_New_Face( p_sys->p_library,
2016 psz_fontfile, i_idx, &p_face ) )
2018 free( psz_fontfile );
2019 free( pp_char_styles );
2020 #if defined(HAVE_FRIBIDI)
2023 free( pi_karaoke_bar );
2024 return VLC_EGENERIC;
2026 free( psz_fontfile );
2030 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2032 /* We've loaded a font face which is unhelpful for actually
2033 * rendering text - fallback to the default one.
2035 FT_Done_Face( p_face );
2039 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2040 ft_encoding_unicode ) ||
2041 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2042 p_style->i_font_size ) )
2044 if( p_face ) FT_Done_Face( p_face );
2045 free( pp_char_styles );
2046 #if defined(HAVE_FRIBIDI)
2049 free( pi_karaoke_bar );
2050 return VLC_EGENERIC;
2052 p_sys->i_use_kerning =
2053 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2056 uint32_t *psz_unicode = (uint32_t *)
2057 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2060 if( p_face ) FT_Done_Face( p_face );
2061 free( pp_char_styles );
2062 free( psz_unicode );
2063 #if defined(HAVE_FRIBIDI)
2066 free( pi_karaoke_bar );
2069 memcpy( psz_unicode, psz_text + i_prev,
2070 sizeof( uint32_t ) * ( k - i_prev ) );
2071 psz_unicode[ k - i_prev ] = 0;
2072 while( *psz_unicode )
2076 if( !(p_line = NewLine( i_len - i_prev)) )
2078 if( p_face ) FT_Done_Face( p_face );
2079 free( pp_char_styles );
2080 free( psz_unicode );
2081 #if defined(HAVE_FRIBIDI)
2084 free( pi_karaoke_bar );
2087 /* New Color mode only works in YUVA rendering mode --
2088 * (RGB mode has palette constraints on it). We therefore
2089 * need to populate the legacy colour fields also.
2091 p_line->b_new_color_mode = true;
2092 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2093 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2094 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2095 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2096 p_line->p_next = NULL;
2098 i_pen_y += tmp_result.y;
2102 if( p_prev ) p_prev->p_next = p_line;
2103 else *pp_lines = p_line;
2106 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2107 p_style->i_font_color, p_style->b_underline,
2111 p_style->i_karaoke_bg_color,
2112 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2113 &tmp_result ) != VLC_SUCCESS )
2115 if( p_face ) FT_Done_Face( p_face );
2116 free( pp_char_styles );
2117 free( psz_unicode );
2118 #if defined(HAVE_FRIBIDI)
2121 free( pi_karaoke_bar );
2122 return VLC_EGENERIC;
2127 p_result->x = __MAX( p_result->x, tmp_result.x );
2128 p_result->y += tmp_result.y;
2133 if( *psz_unicode == '\n')
2137 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2139 *c_ptr = *(c_ptr+1);
2144 free( psz_unicode );
2145 if( p_face ) FT_Done_Face( p_face );
2149 free( pp_char_styles );
2150 #if defined(HAVE_FRIBIDI)
2155 p_result->x = __MAX( p_result->x, tmp_result.x );
2156 p_result->y += tmp_result.y;
2159 if( pi_karaoke_bar )
2162 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2164 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2166 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2170 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2172 /* 100% BG colour will render faster if we
2173 * instead make it 100% FG colour, so leave
2174 * the ratio alone and copy the value across
2176 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2180 if( pi_karaoke_bar[ i ] & 0x80 )
2182 /* Swap Left and Right sides over for Right aligned
2183 * language text (eg. Arabic, Hebrew)
2185 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2187 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2188 p_line->p_bg_rgb[ k ] = i_tmp;
2190 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2193 /* Jump over the '\n' at the line-end */
2196 free( pi_karaoke_bar );
2202 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2203 subpicture_region_t *p_region_in )
2205 int rv = VLC_SUCCESS;
2206 stream_t *p_sub = NULL;
2207 xml_reader_t *p_xml_reader = NULL;
2209 if( !p_region_in || !p_region_in->psz_html )
2210 return VLC_EGENERIC;
2212 /* Reset the default fontsize in case screen metrics have changed */
2213 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2215 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2216 (uint8_t *) p_region_in->psz_html,
2217 strlen( p_region_in->psz_html ),
2221 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2222 if( p_filter->p_sys->p_xml )
2224 bool b_karaoke = false;
2226 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2229 /* Look for Root Node */
2230 if( xml_ReaderRead( p_xml_reader ) == 1 )
2232 char *psz_node = xml_ReaderName( p_xml_reader );
2234 if( !strcasecmp( "karaoke", psz_node ) )
2236 /* We're going to have to render the text a number
2237 * of times to show the progress marker on the text.
2239 var_SetBool( p_filter, "text-rerender", true );
2242 else if( !strcasecmp( "text", psz_node ) )
2248 /* Only text and karaoke tags are supported */
2249 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2250 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2251 p_xml_reader = NULL;
2263 uint32_t i_runs = 0;
2264 uint32_t i_k_runs = 0;
2265 uint32_t *pi_run_lengths = NULL;
2266 uint32_t *pi_k_run_lengths = NULL;
2267 uint32_t *pi_k_durations = NULL;
2268 ft_style_t **pp_styles = NULL;
2270 line_desc_t *p_lines = NULL;
2272 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2273 sizeof( uint32_t ) );
2278 rv = ProcessNodes( p_filter, p_xml_reader,
2279 p_region_in->p_style, psz_text, &i_len,
2280 &i_runs, &pi_run_lengths, &pp_styles,
2282 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2285 p_region_out->i_x = p_region_in->i_x;
2286 p_region_out->i_y = p_region_in->i_y;
2288 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2290 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2291 pi_run_lengths, pp_styles, &p_lines, &result,
2292 b_karaoke, i_k_runs, pi_k_run_lengths,
2296 for( k=0; k<i_runs; k++)
2297 DeleteStyle( pp_styles[k] );
2299 free( pi_run_lengths );
2302 /* Don't attempt to render text that couldn't be layed out
2305 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2307 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2309 Render( p_filter, p_region_out, p_lines,
2310 result.x, result.y );
2314 RenderYUVA( p_filter, p_region_out, p_lines,
2315 result.x, result.y );
2319 FreeLines( p_lines );
2321 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2324 stream_Delete( p_sub );
2330 static char* FontConfig_Select( FcConfig* priv, const char* family,
2331 bool b_bold, bool b_italic, int *i_idx )
2334 FcPattern *pat, *p_pat;
2338 pat = FcPatternCreate();
2339 if (!pat) return NULL;
2341 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2342 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2343 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2344 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2346 FcDefaultSubstitute( pat );
2348 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2350 FcPatternDestroy( pat );
2354 p_pat = FcFontMatch( priv, pat, &result );
2355 FcPatternDestroy( pat );
2356 if( !p_pat ) return NULL;
2358 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2359 || ( val_b != FcTrue ) )
2361 FcPatternDestroy( p_pat );
2364 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2369 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2371 FcPatternDestroy( p_pat );
2376 if( strcasecmp((const char*)val_s, family ) != 0 )
2377 msg_Warn( p_filter, "fontconfig: selected font family is not"
2378 "the requested one: '%s' != '%s'\n",
2379 (const char*)val_s, family );
2382 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2384 FcPatternDestroy( p_pat );
2388 FcPatternDestroy( p_pat );
2389 return strdup( (const char*)val_s );
2393 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2394 uint32_t **psz_text_out, uint32_t *pi_runs,
2395 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2396 ft_style_t *p_style )
2398 VLC_UNUSED(p_filter);
2399 VLC_UNUSED(psz_text_in);
2400 VLC_UNUSED(psz_text_out);
2401 VLC_UNUSED(pi_runs);
2402 VLC_UNUSED(ppi_run_lengths);
2403 VLC_UNUSED(ppp_styles);
2404 VLC_UNUSED(p_style);
2407 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2408 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2409 bool b_uline, bool b_through )
2412 VLC_UNUSED(p_fonts);
2414 VLC_UNUSED(b_italic);
2415 VLC_UNUSED(b_uline);
2416 VLC_UNUSED(b_through);
2421 static void FreeLine( line_desc_t *p_line )
2424 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2426 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2428 free( p_line->pp_glyphs );
2429 free( p_line->p_glyph_pos );
2430 free( p_line->p_fg_rgb );
2431 free( p_line->p_bg_rgb );
2432 free( p_line->p_fg_bg_ratio );
2433 free( p_line->pi_underline_offset );
2434 free( p_line->pi_underline_thickness );
2438 static void FreeLines( line_desc_t *p_lines )
2440 line_desc_t *p_line, *p_next;
2442 if( !p_lines ) return;
2444 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2446 p_next = p_line->p_next;
2451 static line_desc_t *NewLine( int i_count )
2453 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2455 if( !p_line ) return NULL;
2456 p_line->i_height = 0;
2457 p_line->i_width = 0;
2458 p_line->p_next = NULL;
2460 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2461 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2462 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2463 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2464 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2465 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2466 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2467 if( ( p_line->pp_glyphs == NULL ) ||
2468 ( p_line->p_glyph_pos == NULL ) ||
2469 ( p_line->p_fg_rgb == NULL ) ||
2470 ( p_line->p_bg_rgb == NULL ) ||
2471 ( p_line->p_fg_bg_ratio == NULL ) ||
2472 ( p_line->pi_underline_offset == NULL ) ||
2473 ( p_line->pi_underline_thickness == NULL ) )
2475 free( p_line->pi_underline_thickness );
2476 free( p_line->pi_underline_offset );
2477 free( p_line->p_fg_rgb );
2478 free( p_line->p_bg_rgb );
2479 free( p_line->p_fg_bg_ratio );
2480 free( p_line->p_glyph_pos );
2481 free( p_line->pp_glyphs );
2485 p_line->pp_glyphs[0] = NULL;
2486 p_line->b_new_color_mode = false;
2491 static int GetFontSize( filter_t *p_filter )
2493 filter_sys_t *p_sys = p_filter->p_sys;
2497 if( p_sys->i_default_font_size )
2499 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2500 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2502 i_size = p_sys->i_default_font_size;
2506 var_Get( p_filter, "freetype-rel-fontsize", &val );
2509 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2510 p_filter->p_sys->i_display_height =
2511 p_filter->fmt_out.video.i_height;
2516 msg_Warn( p_filter, "invalid fontsize, using 12" );
2517 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2518 i_size = 12 * val.i_int / 1000;
2525 static int SetFontSize( filter_t *p_filter, int i_size )
2527 filter_sys_t *p_sys = p_filter->p_sys;
2531 i_size = GetFontSize( p_filter );
2533 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2536 p_sys->i_font_size = i_size;
2538 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2540 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2541 return VLC_EGENERIC;
2547 static void YUVFromRGB( uint32_t i_argb,
2548 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2550 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2551 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2552 int i_blue = ( i_argb & 0x000000ff );
2554 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2555 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2556 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2557 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2558 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2559 -585 * i_blue + 4096 + 1048576) >> 13, 240);