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>
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_("Fontfile 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 overriden." )
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 uint16_t *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 */
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 /* Lets find some fontfile from freetype-font variable family */
350 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
354 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
355 _("Building font cache"),
356 _("Please wait while your font cache is rebuilt.\n"
357 "This should take less than few minutes."), NULL );
358 char *path = xmalloc( PATH_MAX + 1 );
359 /* Fontconfig doesnt seem to know where windows fonts are with
360 * current contribs. So just tell default windows font directory
361 * is the place to search fonts
363 GetWindowsDirectory( path, PATH_MAX + 1 );
364 strcat( path, "\\fonts" );
366 dialog_ProgressSet( p_dialog, NULL, 0.4 );
368 FcConfigAppFontAddDir( NULL , path );
373 dialog_ProgressSet( p_dialog, NULL, 0.5 );
377 msg_Dbg( p_filter, "Building font database.");
379 FcConfigBuildFonts( NULL );
382 msg_Dbg( p_filter, "Finished building font database." );
383 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
385 fontpattern = FcPatternCreate();
389 msg_Err( p_filter, "Creating fontpattern failed");
395 dialog_ProgressSet( p_dialog, NULL, 0.7 );
397 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
398 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
399 free( psz_fontsize );
401 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
403 msg_Err( p_filter, "FontSubstitute failed");
406 FcDefaultSubstitute( fontpattern );
410 dialog_ProgressSet( p_dialog, NULL, 0.8 );
412 /* testing fontresult here doesn't do any good really, but maybe it will
413 * in future as fontconfig code doesn't set it in all cases and just
414 * returns NULL or doesn't set to to Match on all Match cases.*/
415 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
416 if( !fontmatch || fontresult == FcResultNoMatch )
418 msg_Err( p_filter, "Fontmatching failed");
422 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
423 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
426 msg_Err( p_filter, "Failed to get fontfile");
430 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
431 p_sys->psz_fontfamily = strdup( psz_fontfamily );
435 dialog_ProgressSet( p_dialog, NULL, 1.0 );
436 dialog_ProgressDestroy( p_dialog );
442 #ifdef HAVE_FONTCONFIG
443 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
444 psz_fontfile = psz_fontfamily;
449 i_error = FT_Init_FreeType( &p_sys->p_library );
452 msg_Err( p_filter, "couldn't initialize freetype" );
456 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
457 fontindex, &p_sys->p_face );
459 if( i_error == FT_Err_Unknown_File_Format )
461 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
466 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
470 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
473 msg_Err( p_filter, "font has no unicode translation table" );
477 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
479 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
482 p_sys->pp_font_attachments = NULL;
483 p_sys->i_font_attachments = 0;
485 p_filter->pf_render_text = RenderText;
486 #ifdef HAVE_FONTCONFIG
487 p_filter->pf_render_html = RenderHtml;
488 FcPatternDestroy( fontmatch );
489 FcPatternDestroy( fontpattern );
491 p_filter->pf_render_html = NULL;
494 free( psz_fontfamily );
495 LoadFontsFromAttachments( p_filter );
500 #ifdef HAVE_FONTCONFIG
501 if( fontmatch ) FcPatternDestroy( fontmatch );
502 if( fontpattern ) FcPatternDestroy( fontpattern );
504 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
505 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
506 free( psz_fontfamily );
511 /*****************************************************************************
512 * Destroy: destroy Clone video thread output method
513 *****************************************************************************
514 * Clean up all data and library connections
515 *****************************************************************************/
516 static void Destroy( vlc_object_t *p_this )
518 filter_t *p_filter = (filter_t *)p_this;
519 filter_sys_t *p_sys = p_filter->p_sys;
521 if( p_sys->pp_font_attachments )
525 for( k = 0; k < p_sys->i_font_attachments; k++ )
526 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
528 free( p_sys->pp_font_attachments );
531 #ifdef HAVE_FONTCONFIG
532 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
533 free( p_sys->psz_fontfamily );
536 /* FcFini asserts calling the subfunction FcCacheFini()
537 * even if no other library functions have been made since FcInit(),
538 * so don't call it. */
540 FT_Done_Face( p_sys->p_face );
541 FT_Done_FreeType( p_sys->p_library );
545 /*****************************************************************************
546 * Make any TTF/OTF fonts present in the attachments of the media file
547 * and store them for later use by the FreeType Engine
548 *****************************************************************************/
549 static int LoadFontsFromAttachments( filter_t *p_filter )
551 filter_sys_t *p_sys = p_filter->p_sys;
552 input_thread_t *p_input;
553 input_attachment_t **pp_attachments;
554 int i_attachments_cnt;
556 int rv = VLC_SUCCESS;
558 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
562 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
564 vlc_object_release(p_input);
568 p_sys->i_font_attachments = 0;
569 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
570 if(! p_sys->pp_font_attachments )
573 for( k = 0; k < i_attachments_cnt; k++ )
575 input_attachment_t *p_attach = pp_attachments[k];
577 if( p_sys->pp_font_attachments )
579 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
580 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
581 ( p_attach->i_data > 0 ) &&
582 ( p_attach->p_data != NULL ) )
584 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
588 vlc_input_attachment_Delete( p_attach );
593 vlc_input_attachment_Delete( p_attach );
596 free( pp_attachments );
598 vlc_object_release(p_input);
603 /*****************************************************************************
604 * Render: place string in picture
605 *****************************************************************************
606 * This function merges the previously rendered freetype glyphs into a picture
607 *****************************************************************************/
608 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
609 line_desc_t *p_line, int i_width, int i_height )
611 VLC_UNUSED(p_filter);
612 static const uint8_t pi_gamma[16] =
613 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
614 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
618 int i, x, y, i_pitch;
619 uint8_t i_y; /* YUV values, derived from incoming RGB */
622 /* Create a new subpicture region */
623 memset( &fmt, 0, sizeof(video_format_t) );
624 fmt.i_chroma = VLC_CODEC_YUVP;
626 fmt.i_width = fmt.i_visible_width = i_width + 4;
627 fmt.i_height = fmt.i_visible_height = i_height + 4;
628 if( p_region->fmt.i_visible_width > 0 )
629 fmt.i_visible_width = p_region->fmt.i_visible_width;
630 if( p_region->fmt.i_visible_height > 0 )
631 fmt.i_visible_height = p_region->fmt.i_visible_height;
632 fmt.i_x_offset = fmt.i_y_offset = 0;
634 assert( !p_region->p_picture );
635 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
636 if( !p_region->p_picture )
640 /* Calculate text color components */
641 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
642 25 * p_line->i_blue + 128) >> 8) + 16;
643 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
644 112 * p_line->i_blue + 128) >> 8) + 128;
645 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
646 18 * p_line->i_blue + 128) >> 8) + 128;
649 fmt.p_palette->i_entries = 16;
650 for( i = 0; i < 8; i++ )
652 fmt.p_palette->palette[i][0] = 0;
653 fmt.p_palette->palette[i][1] = 0x80;
654 fmt.p_palette->palette[i][2] = 0x80;
655 fmt.p_palette->palette[i][3] = pi_gamma[i];
656 fmt.p_palette->palette[i][3] =
657 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
659 for( i = 8; i < fmt.p_palette->i_entries; i++ )
661 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
662 fmt.p_palette->palette[i][1] = i_u;
663 fmt.p_palette->palette[i][2] = i_v;
664 fmt.p_palette->palette[i][3] = pi_gamma[i];
665 fmt.p_palette->palette[i][3] =
666 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
669 p_dst = p_region->p_picture->Y_PIXELS;
670 i_pitch = p_region->p_picture->Y_PITCH;
672 /* Initialize the region pixels */
673 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
675 for( ; p_line != NULL; p_line = p_line->p_next )
677 int i_glyph_tmax = 0;
678 int i_bitmap_offset, i_offset, i_align_offset = 0;
679 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
681 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
682 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
685 if( p_line->i_width < i_width )
687 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
689 i_align_offset = i_width - p_line->i_width;
691 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
693 i_align_offset = ( i_width - p_line->i_width ) / 2;
697 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
699 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
701 i_offset = ( p_line->p_glyph_pos[ i ].y +
702 i_glyph_tmax - p_glyph->top + 2 ) *
703 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
706 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
708 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
710 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
712 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
719 /* Outlining (find something better than nearest neighbour filtering ?) */
722 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
723 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
724 uint8_t left, current;
726 for( y = 1; y < (int)fmt.i_height - 1; y++ )
728 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
729 p_dst += p_region->p_picture->Y_PITCH;
732 for( x = 1; x < (int)fmt.i_width - 1; x++ )
735 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
736 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;
740 memset( p_top, 0, fmt.i_width );
746 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
747 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
748 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
749 int i_glyph_tmax, int i_align_offset,
750 uint8_t i_y, uint8_t i_u, uint8_t i_v,
751 subpicture_region_t *p_region)
755 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
757 p_dst_y = p_region->p_picture->Y_PIXELS;
758 p_dst_u = p_region->p_picture->U_PIXELS;
759 p_dst_v = p_region->p_picture->V_PIXELS;
760 p_dst_a = p_region->p_picture->A_PIXELS;
761 i_pitch = p_region->p_picture->A_PITCH;
763 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
764 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
766 for( y = 0; y < i_line_thickness; y++ )
768 int i_extra = p_this_glyph->bitmap.width;
772 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
773 (p_this_glyph_pos->x + p_this_glyph->left);
775 for( x = 0; x < i_extra; x++ )
779 /* break the underline around the tails of any glyphs which cross it */
780 for( z = x - i_line_thickness;
781 z < x + i_line_thickness && b_ok;
784 if( p_next_glyph && ( z >= i_extra ) )
786 int i_row = i_line_offset + p_next_glyph->top + y;
788 if( ( p_next_glyph->bitmap.rows > i_row ) &&
789 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
794 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
796 int i_row = i_line_offset + p_this_glyph->top + y;
798 if( ( p_this_glyph->bitmap.rows > i_row ) &&
799 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
808 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
809 p_dst_u[i_offset+x] = i_u;
810 p_dst_v[i_offset+x] = i_v;
811 p_dst_a[i_offset+x] = 255;
818 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
820 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
821 int i_pitch = p_region->p_picture->A_PITCH;
824 for( ; p_line != NULL; p_line = p_line->p_next )
826 int i_glyph_tmax=0, i = 0;
827 int i_bitmap_offset, i_offset, i_align_offset = 0;
828 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
830 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
831 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
834 if( p_line->i_width < i_width )
836 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
838 i_align_offset = i_width - p_line->i_width;
840 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
842 i_align_offset = ( i_width - p_line->i_width ) / 2;
846 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
848 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
850 i_offset = ( p_line->p_glyph_pos[ i ].y +
851 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
852 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
853 i_align_offset +xoffset;
855 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
857 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
859 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
860 if( p_dst[i_offset+x] <
861 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
863 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
872 /*****************************************************************************
873 * Render: place string in picture
874 *****************************************************************************
875 * This function merges the previously rendered freetype glyphs into a picture
876 *****************************************************************************/
877 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
878 line_desc_t *p_line, int i_width, int i_height )
880 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
882 int i, x, y, i_pitch, i_alpha;
883 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
885 if( i_width == 0 || i_height == 0 )
888 /* Create a new subpicture region */
889 memset( &fmt, 0, sizeof(video_format_t) );
890 fmt.i_chroma = VLC_CODEC_YUVA;
892 fmt.i_width = fmt.i_visible_width = i_width + 6;
893 fmt.i_height = fmt.i_visible_height = i_height + 6;
894 if( p_region->fmt.i_visible_width > 0 )
895 fmt.i_visible_width = p_region->fmt.i_visible_width;
896 if( p_region->fmt.i_visible_height > 0 )
897 fmt.i_visible_height = p_region->fmt.i_visible_height;
898 fmt.i_x_offset = fmt.i_y_offset = 0;
900 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
901 if( !p_region->p_picture )
905 /* Calculate text color components */
906 YUVFromRGB( (p_line->i_red << 16) |
907 (p_line->i_green << 8) |
910 i_alpha = p_line->i_alpha;
912 p_dst_y = p_region->p_picture->Y_PIXELS;
913 p_dst_u = p_region->p_picture->U_PIXELS;
914 p_dst_v = p_region->p_picture->V_PIXELS;
915 p_dst_a = p_region->p_picture->A_PIXELS;
916 i_pitch = p_region->p_picture->A_PITCH;
918 /* Initialize the region pixels */
919 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
921 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
922 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
923 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
924 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
928 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
929 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
930 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
931 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
933 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
934 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
936 DrawBlack( p_line, i_width, p_region, 0, 0);
937 DrawBlack( p_line, i_width, p_region, -1, 0);
938 DrawBlack( p_line, i_width, p_region, 0, -1);
939 DrawBlack( p_line, i_width, p_region, 1, 0);
940 DrawBlack( p_line, i_width, p_region, 0, 1);
943 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
945 DrawBlack( p_line, i_width, p_region, -1, -1);
946 DrawBlack( p_line, i_width, p_region, -1, 1);
947 DrawBlack( p_line, i_width, p_region, 1, -1);
948 DrawBlack( p_line, i_width, p_region, 1, 1);
950 DrawBlack( p_line, i_width, p_region, -2, 0);
951 DrawBlack( p_line, i_width, p_region, 0, -2);
952 DrawBlack( p_line, i_width, p_region, 2, 0);
953 DrawBlack( p_line, i_width, p_region, 0, 2);
955 DrawBlack( p_line, i_width, p_region, -2, -2);
956 DrawBlack( p_line, i_width, p_region, -2, 2);
957 DrawBlack( p_line, i_width, p_region, 2, -2);
958 DrawBlack( p_line, i_width, p_region, 2, 2);
960 DrawBlack( p_line, i_width, p_region, -3, 0);
961 DrawBlack( p_line, i_width, p_region, 0, -3);
962 DrawBlack( p_line, i_width, p_region, 3, 0);
963 DrawBlack( p_line, i_width, p_region, 0, 3);
966 for( ; p_line != NULL; p_line = p_line->p_next )
968 int i_glyph_tmax = 0;
969 int i_bitmap_offset, i_offset, i_align_offset = 0;
970 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
972 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
973 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
976 if( p_line->i_width < i_width )
978 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
980 i_align_offset = i_width - p_line->i_width;
982 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
984 i_align_offset = ( i_width - p_line->i_width ) / 2;
988 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
990 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
992 i_offset = ( p_line->p_glyph_pos[ i ].y +
993 i_glyph_tmax - p_glyph->top + 3 ) *
994 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
997 if( p_line->b_new_color_mode )
999 /* Every glyph can (and in fact must) have its own color */
1000 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1003 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1005 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1007 uint8_t i_y_local = i_y;
1008 uint8_t i_u_local = i_u;
1009 uint8_t i_v_local = i_v;
1011 if( p_line->p_fg_bg_ratio != 0x00 )
1013 int i_split = p_glyph->bitmap.width *
1014 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1018 YUVFromRGB( p_line->p_bg_rgb[ i ],
1019 &i_y_local, &i_u_local, &i_v_local );
1023 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1025 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1026 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1028 p_dst_u[i_offset+x] = i_u;
1029 p_dst_v[i_offset+x] = i_v;
1031 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1032 p_dst_a[i_offset+x] = 0xff;
1035 i_offset += i_pitch;
1038 if( p_line->pi_underline_thickness[ i ] )
1040 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1041 p_line->pi_underline_offset[ i ],
1042 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1043 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1044 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1045 i_glyph_tmax, i_align_offset,
1052 /* Apply the alpha setting */
1053 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1054 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1060 * This function renders a text subpicture region into another one.
1061 * It also calculates the size needed for this string, and renders the
1062 * needed glyphs into memory. It is used as pf_add_string callback in
1063 * the vout method by this module
1065 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1066 subpicture_region_t *p_region_in )
1068 filter_sys_t *p_sys = p_filter->p_sys;
1069 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1070 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1071 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1072 int i_string_length;
1074 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1075 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1085 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1086 psz_string = p_region_in->psz_text;
1087 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1089 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1090 i_scale = val.i_int;
1092 if( p_region_in->p_style )
1094 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1095 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1096 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1100 i_font_color = p_sys->i_font_color;
1101 i_font_alpha = 255 - p_sys->i_font_opacity;
1102 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1105 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1106 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1107 SetFontSize( p_filter, i_font_size );
1109 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1110 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1111 i_blue = i_font_color & 0x000000FF;
1113 result.x = result.y = 0;
1114 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1116 psz_unicode = psz_unicode_orig =
1117 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1118 if( psz_unicode == NULL )
1120 #if defined(WORDS_BIGENDIAN)
1121 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1123 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1125 if( iconv_handle == (vlc_iconv_t)-1 )
1127 msg_Warn( p_filter, "unable to do conversion" );
1133 const char *p_in_buffer = psz_string;
1134 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1135 i_in_bytes = strlen( psz_string );
1136 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1137 i_out_bytes_left = i_out_bytes;
1138 p_out_buffer = (char *)psz_unicode;
1139 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1141 &p_out_buffer, &i_out_bytes_left );
1143 vlc_iconv_close( iconv_handle );
1147 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1148 "bytes left %u", (unsigned)i_in_bytes );
1151 *(uint32_t*)p_out_buffer = 0;
1152 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1155 #if defined(HAVE_FRIBIDI)
1157 uint32_t *p_fribidi_string;
1158 int32_t start_pos, pos = 0;
1160 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1161 if( !p_fribidi_string )
1164 /* Do bidi conversion line-by-line */
1165 while( pos < i_string_length )
1167 while( pos < i_string_length )
1169 i_char = psz_unicode[pos];
1170 if (i_char != '\r' && i_char != '\n')
1172 p_fribidi_string[pos] = i_char;
1176 while( pos < i_string_length )
1178 i_char = psz_unicode[pos];
1179 if (i_char == '\r' || i_char == '\n')
1183 if (pos > start_pos)
1185 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1186 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1189 (FriBidiChar*)p_fribidi_string + start_pos,
1194 free( psz_unicode_orig );
1195 psz_unicode = psz_unicode_orig = p_fribidi_string;
1196 p_fribidi_string[ i_string_length ] = 0;
1200 /* Calculate relative glyph positions and a bounding box for the
1202 if( !(p_line = NewLine( strlen( psz_string ))) )
1205 i_pen_x = i_pen_y = 0;
1207 psz_line_start = psz_unicode;
1209 #define face p_sys->p_face
1210 #define glyph face->glyph
1212 while( *psz_unicode )
1214 i_char = *psz_unicode++;
1215 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1220 if( i_char == '\n' )
1222 psz_line_start = psz_unicode;
1223 if( !(p_next = NewLine( strlen( psz_string ))) )
1225 p_line->p_next = p_next;
1226 p_line->i_width = line.xMax;
1227 p_line->i_height = face->size->metrics.height >> 6;
1228 p_line->pp_glyphs[ i ] = NULL;
1229 p_line->i_alpha = i_font_alpha;
1230 p_line->i_red = i_red;
1231 p_line->i_green = i_green;
1232 p_line->i_blue = i_blue;
1235 result.x = __MAX( result.x, line.xMax );
1236 result.y += face->size->metrics.height >> 6;
1239 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1240 i_pen_y += face->size->metrics.height >> 6;
1242 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1247 i_glyph_index = FT_Get_Char_Index( face, i_char );
1248 if( p_sys->i_use_kerning && i_glyph_index
1252 FT_Get_Kerning( face, i_previous, i_glyph_index,
1253 ft_kerning_default, &delta );
1254 i_pen_x += delta.x >> 6;
1257 p_line->p_glyph_pos[ i ].x = i_pen_x;
1258 p_line->p_glyph_pos[ i ].y = i_pen_y;
1259 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1262 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1266 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1269 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1273 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1274 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1277 FT_Done_Glyph( tmp_glyph );
1280 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1283 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1284 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1285 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1287 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1288 p_line->pp_glyphs[ i ] = NULL;
1290 p_line = NewLine( strlen( psz_string ));
1291 if( p_prev ) p_prev->p_next = p_line;
1292 else p_lines = p_line;
1294 uint32_t *psz_unicode_saved = psz_unicode;
1295 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1299 if( psz_unicode == psz_line_start )
1300 { /* try harder to break that line */
1301 psz_unicode = psz_unicode_saved;
1302 while( psz_unicode > psz_line_start &&
1303 *psz_unicode != '_' && *psz_unicode != '/' &&
1304 *psz_unicode != '\\' && *psz_unicode != '.' )
1309 if( psz_unicode == psz_line_start )
1311 msg_Warn( p_filter, "unbreakable string" );
1316 *psz_unicode = '\n';
1318 psz_unicode = psz_line_start;
1321 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1324 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1325 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1327 i_previous = i_glyph_index;
1328 i_pen_x += glyph->advance.x >> 6;
1332 p_line->i_width = line.xMax;
1333 p_line->i_height = face->size->metrics.height >> 6;
1334 p_line->pp_glyphs[ i ] = NULL;
1335 p_line->i_alpha = i_font_alpha;
1336 p_line->i_red = i_red;
1337 p_line->i_green = i_green;
1338 p_line->i_blue = i_blue;
1339 result.x = __MAX( result.x, line.xMax );
1340 result.y += line.yMax - line.yMin;
1345 p_region_out->i_x = p_region_in->i_x;
1346 p_region_out->i_y = p_region_in->i_y;
1348 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1349 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1351 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1353 free( psz_unicode_orig );
1354 FreeLines( p_lines );
1358 free( psz_unicode_orig );
1359 FreeLines( p_lines );
1360 return VLC_EGENERIC;
1363 #ifdef HAVE_FONTCONFIG
1364 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1365 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1366 bool b_italic, bool b_uline )
1368 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1372 p_style->i_font_size = i_font_size;
1373 p_style->i_font_color = i_font_color;
1374 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1375 p_style->b_italic = b_italic;
1376 p_style->b_bold = b_bold;
1377 p_style->b_underline = b_uline;
1379 p_style->psz_fontname = strdup( psz_fontname );
1384 static void DeleteStyle( ft_style_t *p_style )
1388 free( p_style->psz_fontname );
1393 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1400 if(( s1->i_font_size == s2->i_font_size ) &&
1401 ( s1->i_font_color == s2->i_font_color ) &&
1402 ( s1->b_italic == s2->b_italic ) &&
1403 ( s1->b_bold == s2->b_bold ) &&
1404 ( s1->b_underline == s2->b_underline ) &&
1405 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1412 static void IconvText( filter_t *p_filter, const char *psz_string,
1413 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1415 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1417 /* If memory hasn't been allocated for our output string, allocate it here
1418 * - the calling function must now be responsible for freeing it.
1420 if( !*ppsz_unicode )
1421 *ppsz_unicode = (uint32_t *)
1422 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1424 /* We don't need to handle a NULL pointer in *ppsz_unicode
1425 * if we are instead testing for a non NULL value like we are here */
1429 #if defined(WORDS_BIGENDIAN)
1430 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1432 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1434 if( iconv_handle != (vlc_iconv_t)-1 )
1436 char *p_in_buffer, *p_out_buffer;
1437 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1438 i_in_bytes = strlen( psz_string );
1439 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1440 i_out_bytes_left = i_out_bytes;
1441 p_in_buffer = (char *) psz_string;
1442 p_out_buffer = (char *) *ppsz_unicode;
1443 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1444 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1446 vlc_iconv_close( iconv_handle );
1450 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1451 "bytes left %u", (unsigned)i_in_bytes );
1455 *(uint32_t*)p_out_buffer = 0;
1457 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1462 msg_Warn( p_filter, "unable to do conversion" );
1467 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1468 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1471 ft_style_t *p_style = NULL;
1473 char *psz_fontname = NULL;
1474 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1475 uint32_t i_karaoke_bg_color = i_font_color;
1476 int i_font_size = p_sys->i_font_size;
1478 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1479 &i_font_color, &i_karaoke_bg_color ))
1481 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1482 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1487 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1488 bool b_uline, int i_karaoke_bgcolor,
1489 line_desc_t *p_line, uint32_t *psz_unicode,
1490 int *pi_pen_x, int i_pen_y, int *pi_start,
1491 FT_Vector *p_result )
1496 bool b_first_on_line = true;
1499 int i_pen_x_start = *pi_pen_x;
1501 uint32_t *psz_unicode_start = psz_unicode;
1503 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1505 /* Account for part of line already in position */
1506 for( i=0; i<*pi_start; i++ )
1510 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1511 ft_glyph_bbox_pixels, &glyph_size );
1513 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1514 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1515 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1516 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1522 b_first_on_line = false;
1524 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1530 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1531 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1535 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1536 ft_kerning_default, &delta );
1537 *pi_pen_x += delta.x >> 6;
1539 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1540 p_line->p_glyph_pos[ i ].y = i_pen_y;
1542 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1546 "unable to render text FT_Load_Glyph returned %d", i_error );
1547 p_line->pp_glyphs[ i ] = NULL;
1548 return VLC_EGENERIC;
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 );
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;
1577 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1578 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1579 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1580 p_line->p_fg_bg_ratio[ i ] = 0x00;
1582 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1583 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1584 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1586 for( ; i >= *pi_start; i-- )
1587 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1590 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1594 if( psz_unicode == psz_unicode_start )
1596 if( b_first_on_line )
1598 msg_Warn( p_filter, "unbreakable string" );
1599 p_line->pp_glyphs[ i ] = NULL;
1600 return VLC_EGENERIC;
1602 *pi_pen_x = i_pen_x_start;
1604 p_line->i_width = line.xMax;
1605 p_line->i_height = __MAX( p_line->i_height,
1606 p_face->size->metrics.height >> 6 );
1607 p_line->pp_glyphs[ i ] = NULL;
1609 p_result->x = __MAX( p_result->x, line.xMax );
1610 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1611 i_yMax - i_yMin ) );
1616 *psz_unicode = '\n';
1618 psz_unicode = psz_unicode_start;
1619 *pi_pen_x = i_pen_x_start;
1627 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1628 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1630 i_previous = i_glyph_index;
1631 *pi_pen_x += p_face->glyph->advance.x >> 6;
1634 p_line->i_width = line.xMax;
1635 p_line->i_height = __MAX( p_line->i_height,
1636 p_face->size->metrics.height >> 6 );
1637 p_line->pp_glyphs[ i ] = NULL;
1639 p_result->x = __MAX( p_result->x, line.xMax );
1640 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1641 line.yMax - line.yMin ) );
1645 /* Get rid of any text processed - if necessary repositioning
1646 * at the start of a new line of text
1650 *psz_unicode_start = '\0';
1652 else if( psz_unicode > psz_unicode_start )
1654 for( i=0; psz_unicode[ i ]; i++ )
1655 psz_unicode_start[ i ] = psz_unicode[ i ];
1656 psz_unicode_start[ i ] = '\0';
1662 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1663 uint32_t **psz_text_out, uint32_t *pi_runs,
1664 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1665 ft_style_t *p_style )
1667 uint32_t i_string_length = 0;
1669 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1670 *psz_text_out += i_string_length;
1672 if( ppp_styles && ppi_run_lengths )
1676 /* XXX this logic looks somewhat broken */
1680 *ppp_styles = realloc_or_free( *ppp_styles,
1681 *pi_runs * sizeof( ft_style_t * ) );
1683 else if( *pi_runs == 1 )
1685 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1688 /* We have just malloc'ed this memory successfully -
1689 * *pi_runs HAS to be within the memory area of *ppp_styles */
1692 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1696 /* XXX more iffy logic */
1698 if( *ppi_run_lengths )
1700 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1701 *pi_runs * sizeof( uint32_t ) );
1703 else if( *pi_runs == 1 )
1705 *ppi_run_lengths = (uint32_t *)
1706 malloc( *pi_runs * sizeof( uint32_t ) );
1709 /* same remarks here */
1710 if( *ppi_run_lengths )
1712 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1715 /* If we couldn't use the p_style argument due to memory allocation
1716 * problems above, release it here.
1718 if( p_style ) DeleteStyle( p_style );
1721 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1725 for( k=0; k < p_sys->i_font_attachments; k++ )
1727 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1729 FT_Face p_face = NULL;
1731 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1739 bool match = !strcasecmp( p_face->family_name,
1740 p_style->psz_fontname );
1742 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1743 match = match && p_style->b_bold;
1745 match = match && !p_style->b_bold;
1747 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1748 match = match && p_style->b_italic;
1750 match = match && !p_style->b_italic;
1758 FT_Done_Face( p_face );
1763 return VLC_EGENERIC;
1766 static int ProcessLines( filter_t *p_filter,
1771 uint32_t *pi_run_lengths,
1772 ft_style_t **pp_styles,
1773 line_desc_t **pp_lines,
1775 FT_Vector *p_result,
1779 uint32_t *pi_k_run_lengths,
1780 uint32_t *pi_k_durations )
1782 filter_sys_t *p_sys = p_filter->p_sys;
1783 ft_style_t **pp_char_styles;
1784 int *p_new_positions = NULL;
1785 int8_t *p_levels = NULL;
1786 uint8_t *pi_karaoke_bar = NULL;
1790 /* Assign each character in the text string its style explicitly, so that
1791 * after the characters have been shuffled around by Fribidi, we can re-apply
1792 * the styles, and to simplify the calculation of runs within a line.
1794 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1795 if( !pp_char_styles )
1800 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1801 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1802 * we just won't be able to display the progress bar; at least we'll
1808 for( j = 0; j < i_runs; j++ )
1809 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1810 pp_char_styles[ i++ ] = pp_styles[ j ];
1812 #if defined(HAVE_FRIBIDI)
1814 ft_style_t **pp_char_styles_new;
1815 int *p_old_positions;
1816 uint32_t *p_fribidi_string;
1817 int start_pos, pos = 0;
1819 pp_char_styles_new = (ft_style_t **)
1820 malloc( i_len * sizeof( ft_style_t * ));
1822 p_fribidi_string = (uint32_t *)
1823 malloc( (i_len + 1) * sizeof(uint32_t) );
1824 p_old_positions = (int *)
1825 malloc( (i_len + 1) * sizeof( int ) );
1826 p_new_positions = (int *)
1827 malloc( (i_len + 1) * sizeof( int ) );
1828 p_levels = (int8_t *)
1829 malloc( (i_len + 1) * sizeof( int8_t ) );
1831 if( ! pp_char_styles_new ||
1832 ! p_fribidi_string ||
1833 ! p_old_positions ||
1834 ! p_new_positions ||
1838 free( p_old_positions );
1839 free( p_new_positions );
1840 free( p_fribidi_string );
1841 free( pp_char_styles_new );
1842 free( pi_karaoke_bar );
1844 free( pp_char_styles );
1848 /* Do bidi conversion line-by-line */
1851 while(pos < i_len) {
1852 if (psz_text[pos] != '\n')
1854 p_fribidi_string[pos] = psz_text[pos];
1855 pp_char_styles_new[pos] = pp_char_styles[pos];
1856 p_new_positions[pos] = pos;
1861 while(pos < i_len) {
1862 if (psz_text[pos] == '\n')
1866 if (pos > start_pos)
1868 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1869 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1870 pos - start_pos, &base_dir,
1871 (FriBidiChar*)p_fribidi_string + start_pos,
1872 p_new_positions + start_pos,
1874 p_levels + start_pos );
1875 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1877 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1878 p_old_positions[ j - start_pos ] ];
1879 p_new_positions[ j ] += start_pos;
1883 free( p_old_positions );
1884 free( pp_char_styles );
1885 pp_char_styles = pp_char_styles_new;
1886 psz_text = p_fribidi_string;
1887 p_fribidi_string[ i_len ] = 0;
1890 /* Work out the karaoke */
1891 if( pi_karaoke_bar )
1893 int64_t i_last_duration = 0;
1894 int64_t i_duration = 0;
1895 int64_t i_start_pos = 0;
1896 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1898 for( k = 0; k< i_k_runs; k++ )
1900 double fraction = 0.0;
1902 i_duration += pi_k_durations[ k ];
1904 if( i_duration < i_elapsed )
1906 /* Completely finished this run-length -
1907 * let it render normally */
1911 else if( i_elapsed < i_last_duration )
1913 /* Haven't got up to this segment yet -
1914 * render it completely in karaoke BG mode */
1920 /* Partway through this run */
1922 fraction = (double)(i_elapsed - i_last_duration) /
1923 (double)pi_k_durations[ k ];
1925 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1927 double shade = pi_k_run_lengths[ k ] * fraction;
1929 if( p_new_positions )
1930 j = p_new_positions[ i_start_pos + i ];
1932 j = i_start_pos + i;
1934 if( i < (uint32_t)shade )
1935 pi_karaoke_bar[ j ] = 0xff;
1936 else if( (double)i > shade )
1937 pi_karaoke_bar[ j ] = 0x00;
1940 shade -= (int)shade;
1941 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1942 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1946 i_last_duration = i_duration;
1947 i_start_pos += pi_k_run_lengths[ k ];
1951 free( p_new_positions );
1953 FT_Vector tmp_result;
1955 line_desc_t *p_line = NULL;
1956 line_desc_t *p_prev = NULL;
1962 p_result->x = p_result->y = 0;
1963 tmp_result.x = tmp_result.y = 0;
1966 for( k = 0; k <= (uint32_t) i_len; k++ )
1968 if( ( k == (uint32_t) i_len ) ||
1970 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1972 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1974 /* End of the current style run */
1975 FT_Face p_face = NULL;
1978 /* Look for a match amongst our attachments first */
1979 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1983 char *psz_fontfile = NULL;
1985 psz_fontfile = FontConfig_Select( NULL,
1986 p_style->psz_fontname,
1990 if( psz_fontfile && ! *psz_fontfile )
1992 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1993 " so using default font", p_style->psz_fontname,
1994 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1995 (p_style->b_bold ? "(Bold)" :
1996 (p_style->b_italic ? "(Italic)" : ""))) );
1997 free( psz_fontfile );
1998 psz_fontfile = NULL;
2003 if( FT_New_Face( p_sys->p_library,
2004 psz_fontfile, i_idx, &p_face ) )
2006 free( psz_fontfile );
2007 free( pp_char_styles );
2008 #if defined(HAVE_FRIBIDI)
2011 free( pi_karaoke_bar );
2012 return VLC_EGENERIC;
2014 free( psz_fontfile );
2018 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2020 /* We've loaded a font face which is unhelpful for actually
2021 * rendering text - fallback to the default one.
2023 FT_Done_Face( p_face );
2027 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2028 ft_encoding_unicode ) ||
2029 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2030 p_style->i_font_size ) )
2032 if( p_face ) FT_Done_Face( p_face );
2033 free( pp_char_styles );
2034 #if defined(HAVE_FRIBIDI)
2037 free( pi_karaoke_bar );
2038 return VLC_EGENERIC;
2040 p_sys->i_use_kerning =
2041 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2044 uint32_t *psz_unicode = (uint32_t *)
2045 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2048 if( p_face ) FT_Done_Face( p_face );
2049 free( pp_char_styles );
2050 free( psz_unicode );
2051 #if defined(HAVE_FRIBIDI)
2054 free( pi_karaoke_bar );
2057 memcpy( psz_unicode, psz_text + i_prev,
2058 sizeof( uint32_t ) * ( k - i_prev ) );
2059 psz_unicode[ k - i_prev ] = 0;
2060 while( *psz_unicode )
2064 if( !(p_line = NewLine( i_len - i_prev)) )
2066 if( p_face ) FT_Done_Face( p_face );
2067 free( pp_char_styles );
2068 free( psz_unicode );
2069 #if defined(HAVE_FRIBIDI)
2072 free( pi_karaoke_bar );
2075 /* New Color mode only works in YUVA rendering mode --
2076 * (RGB mode has palette constraints on it). We therefore
2077 * need to populate the legacy colour fields also.
2079 p_line->b_new_color_mode = true;
2080 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2081 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2082 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2083 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2084 p_line->p_next = NULL;
2086 i_pen_y += tmp_result.y;
2090 if( p_prev ) p_prev->p_next = p_line;
2091 else *pp_lines = p_line;
2094 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2095 p_style->i_font_color, p_style->b_underline,
2096 p_style->i_karaoke_bg_color,
2097 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2098 &tmp_result ) != VLC_SUCCESS )
2100 if( p_face ) FT_Done_Face( p_face );
2101 free( pp_char_styles );
2102 free( psz_unicode );
2103 #if defined(HAVE_FRIBIDI)
2106 free( pi_karaoke_bar );
2107 return VLC_EGENERIC;
2112 p_result->x = __MAX( p_result->x, tmp_result.x );
2113 p_result->y += tmp_result.y;
2118 if( *psz_unicode == '\n')
2122 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2124 *c_ptr = *(c_ptr+1);
2129 free( psz_unicode );
2130 if( p_face ) FT_Done_Face( p_face );
2134 free( pp_char_styles );
2135 #if defined(HAVE_FRIBIDI)
2140 p_result->x = __MAX( p_result->x, tmp_result.x );
2141 p_result->y += tmp_result.y;
2144 if( pi_karaoke_bar )
2147 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2149 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2151 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2155 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2157 /* 100% BG colour will render faster if we
2158 * instead make it 100% FG colour, so leave
2159 * the ratio alone and copy the value across
2161 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2165 if( pi_karaoke_bar[ i ] & 0x80 )
2167 /* Swap Left and Right sides over for Right aligned
2168 * language text (eg. Arabic, Hebrew)
2170 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2172 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2173 p_line->p_bg_rgb[ k ] = i_tmp;
2175 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2178 /* Jump over the '\n' at the line-end */
2181 free( pi_karaoke_bar );
2187 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2188 subpicture_region_t *p_region_in )
2190 int rv = VLC_SUCCESS;
2191 stream_t *p_sub = NULL;
2192 xml_reader_t *p_xml_reader = NULL;
2194 if( !p_region_in || !p_region_in->psz_html )
2195 return VLC_EGENERIC;
2197 /* Reset the default fontsize in case screen metrics have changed */
2198 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2200 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2201 (uint8_t *) p_region_in->psz_html,
2202 strlen( p_region_in->psz_html ),
2206 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2207 if( p_filter->p_sys->p_xml )
2209 bool b_karaoke = false;
2211 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2214 /* Look for Root Node */
2215 if( xml_ReaderRead( p_xml_reader ) == 1 )
2217 char *psz_node = xml_ReaderName( p_xml_reader );
2219 if( !strcasecmp( "karaoke", psz_node ) )
2221 /* We're going to have to render the text a number
2222 * of times to show the progress marker on the text.
2224 var_SetBool( p_filter, "text-rerender", true );
2227 else if( !strcasecmp( "text", psz_node ) )
2233 /* Only text and karaoke tags are supported */
2234 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2235 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2236 p_xml_reader = NULL;
2248 uint32_t i_runs = 0;
2249 uint32_t i_k_runs = 0;
2250 uint32_t *pi_run_lengths = NULL;
2251 uint32_t *pi_k_run_lengths = NULL;
2252 uint32_t *pi_k_durations = NULL;
2253 ft_style_t **pp_styles = NULL;
2255 line_desc_t *p_lines = NULL;
2257 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2258 sizeof( uint32_t ) );
2263 rv = ProcessNodes( p_filter, p_xml_reader,
2264 p_region_in->p_style, psz_text, &i_len,
2265 &i_runs, &pi_run_lengths, &pp_styles,
2267 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2270 p_region_out->i_x = p_region_in->i_x;
2271 p_region_out->i_y = p_region_in->i_y;
2273 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2275 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2276 pi_run_lengths, pp_styles, &p_lines, &result,
2277 b_karaoke, i_k_runs, pi_k_run_lengths,
2281 for( k=0; k<i_runs; k++)
2282 DeleteStyle( pp_styles[k] );
2284 free( pi_run_lengths );
2287 /* Don't attempt to render text that couldn't be layed out
2290 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2292 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2294 Render( p_filter, p_region_out, p_lines,
2295 result.x, result.y );
2299 RenderYUVA( p_filter, p_region_out, p_lines,
2300 result.x, result.y );
2304 FreeLines( p_lines );
2306 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2309 stream_Delete( p_sub );
2315 static char* FontConfig_Select( FcConfig* priv, const char* family,
2316 bool b_bold, bool b_italic, int *i_idx )
2319 FcPattern *pat, *p_pat;
2323 pat = FcPatternCreate();
2324 if (!pat) return NULL;
2326 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2327 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2328 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2329 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2331 FcDefaultSubstitute( pat );
2333 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2335 FcPatternDestroy( pat );
2339 p_pat = FcFontMatch( priv, pat, &result );
2340 FcPatternDestroy( pat );
2341 if( !p_pat ) return NULL;
2343 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2344 || ( val_b != FcTrue ) )
2346 FcPatternDestroy( p_pat );
2349 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2354 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2356 FcPatternDestroy( p_pat );
2361 if( strcasecmp((const char*)val_s, family ) != 0 )
2362 msg_Warn( p_filter, "fontconfig: selected font family is not"
2363 "the requested one: '%s' != '%s'\n",
2364 (const char*)val_s, family );
2367 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2369 FcPatternDestroy( p_pat );
2373 FcPatternDestroy( p_pat );
2374 return strdup( (const char*)val_s );
2378 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2379 uint32_t **psz_text_out, uint32_t *pi_runs,
2380 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2381 ft_style_t *p_style )
2383 VLC_UNUSED(p_filter);
2384 VLC_UNUSED(psz_text_in);
2385 VLC_UNUSED(psz_text_out);
2386 VLC_UNUSED(pi_runs);
2387 VLC_UNUSED(ppi_run_lengths);
2388 VLC_UNUSED(ppp_styles);
2389 VLC_UNUSED(p_style);
2392 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2393 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2397 VLC_UNUSED(p_fonts);
2399 VLC_UNUSED(b_italic);
2400 VLC_UNUSED(b_uline);
2405 static void FreeLine( line_desc_t *p_line )
2408 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2410 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2412 free( p_line->pp_glyphs );
2413 free( p_line->p_glyph_pos );
2414 free( p_line->p_fg_rgb );
2415 free( p_line->p_bg_rgb );
2416 free( p_line->p_fg_bg_ratio );
2417 free( p_line->pi_underline_offset );
2418 free( p_line->pi_underline_thickness );
2422 static void FreeLines( line_desc_t *p_lines )
2424 line_desc_t *p_line, *p_next;
2426 if( !p_lines ) return;
2428 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2430 p_next = p_line->p_next;
2435 static line_desc_t *NewLine( int i_count )
2437 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2439 if( !p_line ) return NULL;
2440 p_line->i_height = 0;
2441 p_line->i_width = 0;
2442 p_line->p_next = NULL;
2444 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2445 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2446 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2447 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2448 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2449 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2450 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2451 if( ( p_line->pp_glyphs == NULL ) ||
2452 ( p_line->p_glyph_pos == NULL ) ||
2453 ( p_line->p_fg_rgb == NULL ) ||
2454 ( p_line->p_bg_rgb == NULL ) ||
2455 ( p_line->p_fg_bg_ratio == NULL ) ||
2456 ( p_line->pi_underline_offset == NULL ) ||
2457 ( p_line->pi_underline_thickness == NULL ) )
2459 free( p_line->pi_underline_thickness );
2460 free( p_line->pi_underline_offset );
2461 free( p_line->p_fg_rgb );
2462 free( p_line->p_bg_rgb );
2463 free( p_line->p_fg_bg_ratio );
2464 free( p_line->p_glyph_pos );
2465 free( p_line->pp_glyphs );
2469 p_line->pp_glyphs[0] = NULL;
2470 p_line->b_new_color_mode = false;
2475 static int GetFontSize( filter_t *p_filter )
2477 filter_sys_t *p_sys = p_filter->p_sys;
2481 if( p_sys->i_default_font_size )
2483 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2484 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2486 i_size = p_sys->i_default_font_size;
2490 var_Get( p_filter, "freetype-rel-fontsize", &val );
2493 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2494 p_filter->p_sys->i_display_height =
2495 p_filter->fmt_out.video.i_height;
2500 msg_Warn( p_filter, "invalid fontsize, using 12" );
2501 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2502 i_size = 12 * val.i_int / 1000;
2509 static int SetFontSize( filter_t *p_filter, int i_size )
2511 filter_sys_t *p_sys = p_filter->p_sys;
2515 i_size = GetFontSize( p_filter );
2517 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2520 p_sys->i_font_size = i_size;
2522 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2524 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2525 return VLC_EGENERIC;
2531 static void YUVFromRGB( uint32_t i_argb,
2532 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2534 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2535 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2536 int i_blue = ( i_argb & 0x000000ff );
2538 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2539 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2540 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2541 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2542 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2543 -585 * i_blue + 4096 + 1048576) >> 13, 240);