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 /* Lets find some fontfile from freetype-font variable family */
351 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
355 msg_Dbg( p_filter, "Building font databases.");
360 dialog_progress_bar_t *p_dialog = NULL;
361 FcConfig *fcConfig = FcInitLoadConfig();
363 p_dialog = dialog_ProgressCreate( p_filter,
364 _("Building font cache"),
365 _("Please wait while your font cache is rebuilt.\n"
366 "This should take less than a few minutes."), NULL );
369 dialog_ProgressSet( p_dialog, NULL, 0.5 );
371 FcConfigBuildFonts( fcConfig );
373 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
375 fontpattern = FcPatternCreate();
378 msg_Err( p_filter, "Creating fontpattern failed");
384 dialog_ProgressSet( p_dialog, NULL, 0.7 );
386 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
387 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
388 free( psz_fontsize );
390 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
392 msg_Err( p_filter, "FontSubstitute failed");
395 FcDefaultSubstitute( fontpattern );
399 dialog_ProgressSet( p_dialog, NULL, 0.8 );
401 /* testing fontresult here doesn't do any good really, but maybe it will
402 * in future as fontconfig code doesn't set it in all cases and just
403 * returns NULL or doesn't set to to Match on all Match cases.*/
404 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
405 if( !fontmatch || fontresult == FcResultNoMatch )
407 msg_Err( p_filter, "Fontmatching failed");
411 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
412 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
415 msg_Err( p_filter, "Failed to get fontfile");
419 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
420 psz_fontfile ? psz_fontfile : "(null)" );
421 p_sys->psz_fontfamily = strdup( psz_fontfamily );
425 dialog_ProgressSet( p_dialog, NULL, 1.0 );
426 dialog_ProgressDestroy( p_dialog );
433 #ifdef HAVE_FONTCONFIG
434 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
435 psz_fontfile = psz_fontfamily;
440 i_error = FT_Init_FreeType( &p_sys->p_library );
443 msg_Err( p_filter, "couldn't initialize freetype" );
447 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
448 fontindex, &p_sys->p_face );
450 if( i_error == FT_Err_Unknown_File_Format )
452 msg_Err( p_filter, "file %s have unknown format",
453 psz_fontfile ? psz_fontfile : "(null)" );
458 msg_Err( p_filter, "failed to load font file %s",
459 psz_fontfile ? psz_fontfile : "(null)" );
463 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
466 msg_Err( p_filter, "font has no unicode translation table" );
470 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
472 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
475 p_sys->pp_font_attachments = NULL;
476 p_sys->i_font_attachments = 0;
478 p_filter->pf_render_text = RenderText;
479 #ifdef HAVE_FONTCONFIG
480 p_filter->pf_render_html = RenderHtml;
481 FcPatternDestroy( fontmatch );
482 FcPatternDestroy( fontpattern );
484 p_filter->pf_render_html = NULL;
487 free( psz_fontfamily );
488 LoadFontsFromAttachments( p_filter );
493 #ifdef HAVE_FONTCONFIG
494 if( fontmatch ) FcPatternDestroy( fontmatch );
495 if( fontpattern ) FcPatternDestroy( fontpattern );
500 dialog_ProgressDestroy( p_dialog );
503 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
504 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
505 free( psz_fontfamily );
510 /*****************************************************************************
511 * Destroy: destroy Clone video thread output method
512 *****************************************************************************
513 * Clean up all data and library connections
514 *****************************************************************************/
515 static void Destroy( vlc_object_t *p_this )
517 filter_t *p_filter = (filter_t *)p_this;
518 filter_sys_t *p_sys = p_filter->p_sys;
520 if( p_sys->pp_font_attachments )
524 for( k = 0; k < p_sys->i_font_attachments; k++ )
525 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
527 free( p_sys->pp_font_attachments );
530 #ifdef HAVE_FONTCONFIG
531 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
532 free( p_sys->psz_fontfamily );
535 /* FcFini asserts calling the subfunction FcCacheFini()
536 * even if no other library functions have been made since FcInit(),
537 * so don't call it. */
539 FT_Done_Face( p_sys->p_face );
540 FT_Done_FreeType( p_sys->p_library );
544 /*****************************************************************************
545 * Make any TTF/OTF fonts present in the attachments of the media file
546 * and store them for later use by the FreeType Engine
547 *****************************************************************************/
548 static int LoadFontsFromAttachments( filter_t *p_filter )
550 filter_sys_t *p_sys = p_filter->p_sys;
551 input_thread_t *p_input;
552 input_attachment_t **pp_attachments;
553 int i_attachments_cnt;
555 int rv = VLC_SUCCESS;
557 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
561 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
563 vlc_object_release(p_input);
567 p_sys->i_font_attachments = 0;
568 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
569 if(! p_sys->pp_font_attachments )
572 for( k = 0; k < i_attachments_cnt; k++ )
574 input_attachment_t *p_attach = pp_attachments[k];
576 if( p_sys->pp_font_attachments )
578 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
579 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
580 ( p_attach->i_data > 0 ) &&
581 ( p_attach->p_data != NULL ) )
583 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
587 vlc_input_attachment_Delete( p_attach );
592 vlc_input_attachment_Delete( p_attach );
595 free( pp_attachments );
597 vlc_object_release(p_input);
602 /*****************************************************************************
603 * Render: place string in picture
604 *****************************************************************************
605 * This function merges the previously rendered freetype glyphs into a picture
606 *****************************************************************************/
607 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
608 line_desc_t *p_line, int i_width, int i_height )
610 VLC_UNUSED(p_filter);
611 static const uint8_t pi_gamma[16] =
612 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
613 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
617 int i, x, y, i_pitch;
618 uint8_t i_y; /* YUV values, derived from incoming RGB */
621 /* Create a new subpicture region */
622 memset( &fmt, 0, sizeof(video_format_t) );
623 fmt.i_chroma = VLC_CODEC_YUVP;
624 fmt.i_width = fmt.i_visible_width = i_width + 4;
625 fmt.i_height = fmt.i_visible_height = i_height + 4;
626 if( p_region->fmt.i_visible_width > 0 )
627 fmt.i_visible_width = p_region->fmt.i_visible_width;
628 if( p_region->fmt.i_visible_height > 0 )
629 fmt.i_visible_height = p_region->fmt.i_visible_height;
630 fmt.i_x_offset = fmt.i_y_offset = 0;
632 assert( !p_region->p_picture );
633 p_region->p_picture = picture_NewFromFormat( &fmt );
634 if( !p_region->p_picture )
636 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
639 /* Calculate text color components */
640 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
641 25 * p_line->i_blue + 128) >> 8) + 16;
642 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
643 112 * p_line->i_blue + 128) >> 8) + 128;
644 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
645 18 * p_line->i_blue + 128) >> 8) + 128;
648 fmt.p_palette->i_entries = 16;
649 for( i = 0; i < 8; i++ )
651 fmt.p_palette->palette[i][0] = 0;
652 fmt.p_palette->palette[i][1] = 0x80;
653 fmt.p_palette->palette[i][2] = 0x80;
654 fmt.p_palette->palette[i][3] = pi_gamma[i];
655 fmt.p_palette->palette[i][3] =
656 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
658 for( i = 8; i < fmt.p_palette->i_entries; i++ )
660 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
661 fmt.p_palette->palette[i][1] = i_u;
662 fmt.p_palette->palette[i][2] = i_v;
663 fmt.p_palette->palette[i][3] = pi_gamma[i];
664 fmt.p_palette->palette[i][3] =
665 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
668 p_dst = p_region->p_picture->Y_PIXELS;
669 i_pitch = p_region->p_picture->Y_PITCH;
671 /* Initialize the region pixels */
672 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
674 for( ; p_line != NULL; p_line = p_line->p_next )
676 int i_glyph_tmax = 0;
677 int i_bitmap_offset, i_offset, i_align_offset = 0;
678 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
680 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
681 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
684 if( p_line->i_width < i_width )
686 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
688 i_align_offset = i_width - p_line->i_width;
690 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
692 i_align_offset = ( i_width - p_line->i_width ) / 2;
696 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
698 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
700 i_offset = ( p_line->p_glyph_pos[ i ].y +
701 i_glyph_tmax - p_glyph->top + 2 ) *
702 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
705 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
707 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
709 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
711 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
718 /* Outlining (find something better than nearest neighbour filtering ?) */
721 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
722 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
723 uint8_t left, current;
725 for( y = 1; y < (int)fmt.i_height - 1; y++ )
727 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
728 p_dst += p_region->p_picture->Y_PITCH;
731 for( x = 1; x < (int)fmt.i_width - 1; x++ )
734 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
735 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;
739 memset( p_top, 0, fmt.i_width );
745 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
746 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
747 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
748 int i_glyph_tmax, int i_align_offset,
749 uint8_t i_y, uint8_t i_u, uint8_t i_v,
750 subpicture_region_t *p_region)
754 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
756 p_dst_y = p_region->p_picture->Y_PIXELS;
757 p_dst_u = p_region->p_picture->U_PIXELS;
758 p_dst_v = p_region->p_picture->V_PIXELS;
759 p_dst_a = p_region->p_picture->A_PIXELS;
760 i_pitch = p_region->p_picture->A_PITCH;
762 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
763 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
765 for( y = 0; y < i_line_thickness; y++ )
767 int i_extra = p_this_glyph->bitmap.width;
771 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
772 (p_this_glyph_pos->x + p_this_glyph->left);
774 for( x = 0; x < i_extra; x++ )
778 /* break the underline around the tails of any glyphs which cross it */
779 /* Strikethrough doesn't get broken */
780 for( z = x - i_line_thickness;
781 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
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;
891 fmt.i_width = fmt.i_visible_width = i_width + 6;
892 fmt.i_height = fmt.i_visible_height = i_height + 6;
893 if( p_region->fmt.i_visible_width > 0 )
894 fmt.i_visible_width = p_region->fmt.i_visible_width;
895 if( p_region->fmt.i_visible_height > 0 )
896 fmt.i_visible_height = p_region->fmt.i_visible_height;
897 fmt.i_x_offset = fmt.i_y_offset = 0;
899 p_region->p_picture = picture_NewFromFormat( &fmt );
900 if( !p_region->p_picture )
904 /* Calculate text color components */
905 YUVFromRGB( (p_line->i_red << 16) |
906 (p_line->i_green << 8) |
909 i_alpha = p_line->i_alpha;
911 p_dst_y = p_region->p_picture->Y_PIXELS;
912 p_dst_u = p_region->p_picture->U_PIXELS;
913 p_dst_v = p_region->p_picture->V_PIXELS;
914 p_dst_a = p_region->p_picture->A_PIXELS;
915 i_pitch = p_region->p_picture->A_PITCH;
917 /* Initialize the region pixels */
918 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
920 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
921 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
922 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
923 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
927 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
928 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
929 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
930 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
932 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
933 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
935 DrawBlack( p_line, i_width, p_region, 0, 0);
936 DrawBlack( p_line, i_width, p_region, -1, 0);
937 DrawBlack( p_line, i_width, p_region, 0, -1);
938 DrawBlack( p_line, i_width, p_region, 1, 0);
939 DrawBlack( p_line, i_width, p_region, 0, 1);
942 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
944 DrawBlack( p_line, i_width, p_region, -1, -1);
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);
949 DrawBlack( p_line, i_width, p_region, -2, 0);
950 DrawBlack( p_line, i_width, p_region, 0, -2);
951 DrawBlack( p_line, i_width, p_region, 2, 0);
952 DrawBlack( p_line, i_width, p_region, 0, 2);
954 DrawBlack( p_line, i_width, p_region, -2, -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);
959 DrawBlack( p_line, i_width, p_region, -3, 0);
960 DrawBlack( p_line, i_width, p_region, 0, -3);
961 DrawBlack( p_line, i_width, p_region, 3, 0);
962 DrawBlack( p_line, i_width, p_region, 0, 3);
965 for( ; p_line != NULL; p_line = p_line->p_next )
967 int i_glyph_tmax = 0;
968 int i_bitmap_offset, i_offset, i_align_offset = 0;
969 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
971 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
972 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
975 if( p_line->i_width < i_width )
977 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
979 i_align_offset = i_width - p_line->i_width;
981 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
983 i_align_offset = ( i_width - p_line->i_width ) / 2;
987 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
989 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
991 i_offset = ( p_line->p_glyph_pos[ i ].y +
992 i_glyph_tmax - p_glyph->top + 3 ) *
993 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
996 if( p_line->b_new_color_mode )
998 /* Every glyph can (and in fact must) have its own color */
999 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1002 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1004 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1006 uint8_t i_y_local = i_y;
1007 uint8_t i_u_local = i_u;
1008 uint8_t i_v_local = i_v;
1010 if( p_line->p_fg_bg_ratio != 0x00 )
1012 int i_split = p_glyph->bitmap.width *
1013 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1017 YUVFromRGB( p_line->p_bg_rgb[ i ],
1018 &i_y_local, &i_u_local, &i_v_local );
1022 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1024 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1025 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1027 p_dst_u[i_offset+x] = i_u;
1028 p_dst_v[i_offset+x] = i_v;
1030 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1031 p_dst_a[i_offset+x] = 0xff;
1034 i_offset += i_pitch;
1037 if( p_line->pi_underline_thickness[ i ] )
1039 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1040 p_line->pi_underline_offset[ i ],
1041 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1042 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1043 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1044 i_glyph_tmax, i_align_offset,
1051 /* Apply the alpha setting */
1052 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1053 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1059 * This function renders a text subpicture region into another one.
1060 * It also calculates the size needed for this string, and renders the
1061 * needed glyphs into memory. It is used as pf_add_string callback in
1062 * the vout method by this module
1064 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1065 subpicture_region_t *p_region_in )
1067 filter_sys_t *p_sys = p_filter->p_sys;
1068 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1069 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1070 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1071 int i_string_length;
1073 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1074 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1084 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1085 psz_string = p_region_in->psz_text;
1086 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1088 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1089 i_scale = val.i_int;
1091 if( p_region_in->p_style )
1093 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1094 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1095 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1099 i_font_color = p_sys->i_font_color;
1100 i_font_alpha = 255 - p_sys->i_font_opacity;
1101 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1104 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1105 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1106 SetFontSize( p_filter, i_font_size );
1108 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1109 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1110 i_blue = i_font_color & 0x000000FF;
1112 result.x = result.y = 0;
1113 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1115 psz_unicode = psz_unicode_orig =
1116 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1117 if( psz_unicode == NULL )
1119 #if defined(WORDS_BIGENDIAN)
1120 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1122 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1124 if( iconv_handle == (vlc_iconv_t)-1 )
1126 msg_Warn( p_filter, "unable to do conversion" );
1132 const char *p_in_buffer = psz_string;
1133 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1134 i_in_bytes = strlen( psz_string );
1135 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1136 i_out_bytes_left = i_out_bytes;
1137 p_out_buffer = (char *)psz_unicode;
1138 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1140 &p_out_buffer, &i_out_bytes_left );
1142 vlc_iconv_close( iconv_handle );
1146 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1147 "bytes left %u", (unsigned)i_in_bytes );
1150 *(uint32_t*)p_out_buffer = 0;
1151 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1154 #if defined(HAVE_FRIBIDI)
1156 uint32_t *p_fribidi_string;
1157 int32_t start_pos, pos = 0;
1159 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1160 if( !p_fribidi_string )
1163 /* Do bidi conversion line-by-line */
1164 while( pos < i_string_length )
1166 while( pos < i_string_length )
1168 i_char = psz_unicode[pos];
1169 if (i_char != '\r' && i_char != '\n')
1171 p_fribidi_string[pos] = i_char;
1175 while( pos < i_string_length )
1177 i_char = psz_unicode[pos];
1178 if (i_char == '\r' || i_char == '\n')
1182 if (pos > start_pos)
1184 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1185 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1188 (FriBidiChar*)p_fribidi_string + start_pos,
1193 free( psz_unicode_orig );
1194 psz_unicode = psz_unicode_orig = p_fribidi_string;
1195 p_fribidi_string[ i_string_length ] = 0;
1199 /* Calculate relative glyph positions and a bounding box for the
1201 if( !(p_line = NewLine( strlen( psz_string ))) )
1204 i_pen_x = i_pen_y = 0;
1206 psz_line_start = psz_unicode;
1208 #define face p_sys->p_face
1209 #define glyph face->glyph
1211 while( *psz_unicode )
1213 i_char = *psz_unicode++;
1214 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1219 if( i_char == '\n' )
1221 psz_line_start = psz_unicode;
1222 if( !(p_next = NewLine( strlen( psz_string ))) )
1224 p_line->p_next = p_next;
1225 p_line->i_width = line.xMax;
1226 p_line->i_height = face->size->metrics.height >> 6;
1227 p_line->pp_glyphs[ i ] = NULL;
1228 p_line->i_alpha = i_font_alpha;
1229 p_line->i_red = i_red;
1230 p_line->i_green = i_green;
1231 p_line->i_blue = i_blue;
1234 result.x = __MAX( result.x, line.xMax );
1235 result.y += face->size->metrics.height >> 6;
1238 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1239 i_pen_y += face->size->metrics.height >> 6;
1241 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1246 i_glyph_index = FT_Get_Char_Index( face, i_char );
1247 if( p_sys->i_use_kerning && i_glyph_index
1251 FT_Get_Kerning( face, i_previous, i_glyph_index,
1252 ft_kerning_default, &delta );
1253 i_pen_x += delta.x >> 6;
1256 p_line->p_glyph_pos[ i ].x = i_pen_x;
1257 p_line->p_glyph_pos[ i ].y = i_pen_y;
1258 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1261 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1265 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1268 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1272 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1273 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1276 FT_Done_Glyph( tmp_glyph );
1279 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1282 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1283 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1284 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1286 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1287 p_line->pp_glyphs[ i ] = NULL;
1289 p_line = NewLine( strlen( psz_string ));
1290 if( p_prev ) p_prev->p_next = p_line;
1291 else p_lines = p_line;
1293 uint32_t *psz_unicode_saved = psz_unicode;
1294 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1298 if( psz_unicode == psz_line_start )
1299 { /* try harder to break that line */
1300 psz_unicode = psz_unicode_saved;
1301 while( psz_unicode > psz_line_start &&
1302 *psz_unicode != '_' && *psz_unicode != '/' &&
1303 *psz_unicode != '\\' && *psz_unicode != '.' )
1308 if( psz_unicode == psz_line_start )
1310 msg_Warn( p_filter, "unbreakable string" );
1315 *psz_unicode = '\n';
1317 psz_unicode = psz_line_start;
1320 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1323 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1324 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1326 i_previous = i_glyph_index;
1327 i_pen_x += glyph->advance.x >> 6;
1331 p_line->i_width = line.xMax;
1332 p_line->i_height = face->size->metrics.height >> 6;
1333 p_line->pp_glyphs[ i ] = NULL;
1334 p_line->i_alpha = i_font_alpha;
1335 p_line->i_red = i_red;
1336 p_line->i_green = i_green;
1337 p_line->i_blue = i_blue;
1338 result.x = __MAX( result.x, line.xMax );
1339 result.y += line.yMax - line.yMin;
1344 p_region_out->i_x = p_region_in->i_x;
1345 p_region_out->i_y = p_region_in->i_y;
1347 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1348 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1350 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1352 free( psz_unicode_orig );
1353 FreeLines( p_lines );
1357 free( psz_unicode_orig );
1358 FreeLines( p_lines );
1359 return VLC_EGENERIC;
1362 #ifdef HAVE_FONTCONFIG
1363 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1364 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1365 bool b_italic, bool b_uline, bool b_through )
1367 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1371 p_style->i_font_size = i_font_size;
1372 p_style->i_font_color = i_font_color;
1373 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1374 p_style->b_italic = b_italic;
1375 p_style->b_bold = b_bold;
1376 p_style->b_underline = b_uline;
1377 p_style->b_through = b_through;
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_through == s2->b_through ) &&
1404 ( s1->b_bold == s2->b_bold ) &&
1405 ( s1->b_underline == s2->b_underline ) &&
1406 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1413 static void IconvText( filter_t *p_filter, const char *psz_string,
1414 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1416 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1418 /* If memory hasn't been allocated for our output string, allocate it here
1419 * - the calling function must now be responsible for freeing it.
1421 if( !*ppsz_unicode )
1422 *ppsz_unicode = (uint32_t *)
1423 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1425 /* We don't need to handle a NULL pointer in *ppsz_unicode
1426 * if we are instead testing for a non NULL value like we are here */
1430 #if defined(WORDS_BIGENDIAN)
1431 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1433 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1435 if( iconv_handle != (vlc_iconv_t)-1 )
1437 char *p_in_buffer, *p_out_buffer;
1438 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1439 i_in_bytes = strlen( psz_string );
1440 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1441 i_out_bytes_left = i_out_bytes;
1442 p_in_buffer = (char *) psz_string;
1443 p_out_buffer = (char *) *ppsz_unicode;
1444 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1445 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1447 vlc_iconv_close( iconv_handle );
1451 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1452 "bytes left %u", (unsigned)i_in_bytes );
1456 *(uint32_t*)p_out_buffer = 0;
1458 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1463 msg_Warn( p_filter, "unable to do conversion" );
1468 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1469 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1470 bool b_uline, bool b_through )
1472 ft_style_t *p_style = NULL;
1474 char *psz_fontname = NULL;
1475 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1476 uint32_t i_karaoke_bg_color = i_font_color;
1477 int i_font_size = p_sys->i_font_size;
1479 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1480 &i_font_color, &i_karaoke_bg_color ))
1482 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1483 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1488 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1489 bool b_uline, bool b_through, bool b_bold,
1490 bool b_italic, int i_karaoke_bgcolor,
1491 line_desc_t *p_line, uint32_t *psz_unicode,
1492 int *pi_pen_x, int i_pen_y, int *pi_start,
1493 FT_Vector *p_result )
1498 bool b_first_on_line = true;
1501 int i_pen_x_start = *pi_pen_x;
1503 uint32_t *psz_unicode_start = psz_unicode;
1505 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1507 /* Account for part of line already in position */
1508 for( i=0; i<*pi_start; i++ )
1512 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1513 ft_glyph_bbox_pixels, &glyph_size );
1515 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1516 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1517 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1518 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1524 b_first_on_line = false;
1526 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1532 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1533 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1537 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1538 ft_kerning_default, &delta );
1539 *pi_pen_x += delta.x >> 6;
1541 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1542 p_line->p_glyph_pos[ i ].y = i_pen_y;
1544 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1548 "unable to render text FT_Load_Glyph returned %d", i_error );
1549 p_line->pp_glyphs[ i ] = NULL;
1550 return VLC_EGENERIC;
1553 /* Do synthetic styling now that Freetype supports it;
1554 * ie. if the font we have loaded is NOT already in the
1555 * style that the tags want, then switch it on; if they
1556 * are then don't. */
1557 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1558 FT_GlyphSlot_Embolden( p_face->glyph );
1559 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1560 FT_GlyphSlot_Oblique( p_face->glyph );
1562 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1566 "unable to render text FT_Get_Glyph returned %d", i_error );
1567 p_line->pp_glyphs[ i ] = NULL;
1568 return VLC_EGENERIC;
1570 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1571 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1574 FT_Done_Glyph( tmp_glyph );
1577 if( b_uline || b_through )
1579 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1580 p_face->size->metrics.y_scale));
1581 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1582 p_face->size->metrics.y_scale));
1584 p_line->pi_underline_offset[ i ] =
1585 ( aOffset < 0 ) ? -aOffset : aOffset;
1586 p_line->pi_underline_thickness[ i ] =
1587 ( aSize < 0 ) ? -aSize : aSize;
1590 /* Move the baseline to make it strikethrough instead of
1591 * underline. That means that strikethrough takes precedence
1593 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1594 p_face->size->metrics.y_scale));
1596 p_line->pi_underline_offset[ i ] -=
1597 ( aDescent < 0 ) ? -aDescent : aDescent;
1601 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1602 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1603 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1604 p_line->p_fg_bg_ratio[ i ] = 0x00;
1606 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1607 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1608 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1610 for( ; i >= *pi_start; i-- )
1611 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1614 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1618 if( psz_unicode == psz_unicode_start )
1620 if( b_first_on_line )
1622 msg_Warn( p_filter, "unbreakable string" );
1623 p_line->pp_glyphs[ i ] = NULL;
1624 return VLC_EGENERIC;
1626 *pi_pen_x = i_pen_x_start;
1628 p_line->i_width = line.xMax;
1629 p_line->i_height = __MAX( p_line->i_height,
1630 p_face->size->metrics.height >> 6 );
1631 p_line->pp_glyphs[ i ] = NULL;
1633 p_result->x = __MAX( p_result->x, line.xMax );
1634 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1635 i_yMax - i_yMin ) );
1640 *psz_unicode = '\n';
1642 psz_unicode = psz_unicode_start;
1643 *pi_pen_x = i_pen_x_start;
1651 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1652 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1654 i_previous = i_glyph_index;
1655 *pi_pen_x += p_face->glyph->advance.x >> 6;
1658 p_line->i_width = line.xMax;
1659 p_line->i_height = __MAX( p_line->i_height,
1660 p_face->size->metrics.height >> 6 );
1661 p_line->pp_glyphs[ i ] = NULL;
1663 p_result->x = __MAX( p_result->x, line.xMax );
1664 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1665 line.yMax - line.yMin ) );
1669 /* Get rid of any text processed - if necessary repositioning
1670 * at the start of a new line of text
1674 *psz_unicode_start = '\0';
1676 else if( psz_unicode > psz_unicode_start )
1678 for( i=0; psz_unicode[ i ]; i++ )
1679 psz_unicode_start[ i ] = psz_unicode[ i ];
1680 psz_unicode_start[ i ] = '\0';
1686 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1687 uint32_t **psz_text_out, uint32_t *pi_runs,
1688 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1689 ft_style_t *p_style )
1691 uint32_t i_string_length = 0;
1693 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1694 *psz_text_out += i_string_length;
1696 if( ppp_styles && ppi_run_lengths )
1700 /* XXX this logic looks somewhat broken */
1704 *ppp_styles = realloc_or_free( *ppp_styles,
1705 *pi_runs * sizeof( ft_style_t * ) );
1707 else if( *pi_runs == 1 )
1709 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1712 /* We have just malloc'ed this memory successfully -
1713 * *pi_runs HAS to be within the memory area of *ppp_styles */
1716 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1720 /* XXX more iffy logic */
1722 if( *ppi_run_lengths )
1724 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1725 *pi_runs * sizeof( uint32_t ) );
1727 else if( *pi_runs == 1 )
1729 *ppi_run_lengths = (uint32_t *)
1730 malloc( *pi_runs * sizeof( uint32_t ) );
1733 /* same remarks here */
1734 if( *ppi_run_lengths )
1736 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1739 /* If we couldn't use the p_style argument due to memory allocation
1740 * problems above, release it here.
1742 if( p_style ) DeleteStyle( p_style );
1745 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1749 for( k=0; k < p_sys->i_font_attachments; k++ )
1751 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1753 FT_Face p_face = NULL;
1755 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1763 bool match = !strcasecmp( p_face->family_name,
1764 p_style->psz_fontname );
1766 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1767 match = match && p_style->b_bold;
1769 match = match && !p_style->b_bold;
1771 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1772 match = match && p_style->b_italic;
1774 match = match && !p_style->b_italic;
1782 FT_Done_Face( p_face );
1787 return VLC_EGENERIC;
1790 static int ProcessLines( filter_t *p_filter,
1795 uint32_t *pi_run_lengths,
1796 ft_style_t **pp_styles,
1797 line_desc_t **pp_lines,
1799 FT_Vector *p_result,
1803 uint32_t *pi_k_run_lengths,
1804 uint32_t *pi_k_durations )
1806 filter_sys_t *p_sys = p_filter->p_sys;
1807 ft_style_t **pp_char_styles;
1808 int *p_new_positions = NULL;
1809 int8_t *p_levels = NULL;
1810 uint8_t *pi_karaoke_bar = NULL;
1814 /* Assign each character in the text string its style explicitly, so that
1815 * after the characters have been shuffled around by Fribidi, we can re-apply
1816 * the styles, and to simplify the calculation of runs within a line.
1818 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1819 if( !pp_char_styles )
1824 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1825 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1826 * we just won't be able to display the progress bar; at least we'll
1832 for( j = 0; j < i_runs; j++ )
1833 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1834 pp_char_styles[ i++ ] = pp_styles[ j ];
1836 #if defined(HAVE_FRIBIDI)
1838 ft_style_t **pp_char_styles_new;
1839 int *p_old_positions;
1840 uint32_t *p_fribidi_string;
1841 int start_pos, pos = 0;
1843 pp_char_styles_new = (ft_style_t **)
1844 malloc( i_len * sizeof( ft_style_t * ));
1846 p_fribidi_string = (uint32_t *)
1847 malloc( (i_len + 1) * sizeof(uint32_t) );
1848 p_old_positions = (int *)
1849 malloc( (i_len + 1) * sizeof( int ) );
1850 p_new_positions = (int *)
1851 malloc( (i_len + 1) * sizeof( int ) );
1852 p_levels = (int8_t *)
1853 malloc( (i_len + 1) * sizeof( int8_t ) );
1855 if( ! pp_char_styles_new ||
1856 ! p_fribidi_string ||
1857 ! p_old_positions ||
1858 ! p_new_positions ||
1862 free( p_old_positions );
1863 free( p_new_positions );
1864 free( p_fribidi_string );
1865 free( pp_char_styles_new );
1866 free( pi_karaoke_bar );
1868 free( pp_char_styles );
1872 /* Do bidi conversion line-by-line */
1875 while(pos < i_len) {
1876 if (psz_text[pos] != '\n')
1878 p_fribidi_string[pos] = psz_text[pos];
1879 pp_char_styles_new[pos] = pp_char_styles[pos];
1880 p_new_positions[pos] = pos;
1885 while(pos < i_len) {
1886 if (psz_text[pos] == '\n')
1890 if (pos > start_pos)
1892 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1893 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1894 pos - start_pos, &base_dir,
1895 (FriBidiChar*)p_fribidi_string + start_pos,
1896 p_new_positions + start_pos,
1898 p_levels + start_pos );
1899 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1901 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1902 p_old_positions[ j - start_pos ] ];
1903 p_new_positions[ j ] += start_pos;
1907 free( p_old_positions );
1908 free( pp_char_styles );
1909 pp_char_styles = pp_char_styles_new;
1910 psz_text = p_fribidi_string;
1911 p_fribidi_string[ i_len ] = 0;
1914 /* Work out the karaoke */
1915 if( pi_karaoke_bar )
1917 int64_t i_last_duration = 0;
1918 int64_t i_duration = 0;
1919 int64_t i_start_pos = 0;
1920 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1922 for( k = 0; k< i_k_runs; k++ )
1924 double fraction = 0.0;
1926 i_duration += pi_k_durations[ k ];
1928 if( i_duration < i_elapsed )
1930 /* Completely finished this run-length -
1931 * let it render normally */
1935 else if( i_elapsed < i_last_duration )
1937 /* Haven't got up to this segment yet -
1938 * render it completely in karaoke BG mode */
1944 /* Partway through this run */
1946 fraction = (double)(i_elapsed - i_last_duration) /
1947 (double)pi_k_durations[ k ];
1949 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1951 double shade = pi_k_run_lengths[ k ] * fraction;
1953 if( p_new_positions )
1954 j = p_new_positions[ i_start_pos + i ];
1956 j = i_start_pos + i;
1958 if( i < (uint32_t)shade )
1959 pi_karaoke_bar[ j ] = 0xff;
1960 else if( (double)i > shade )
1961 pi_karaoke_bar[ j ] = 0x00;
1964 shade -= (int)shade;
1965 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1966 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1970 i_last_duration = i_duration;
1971 i_start_pos += pi_k_run_lengths[ k ];
1975 free( p_new_positions );
1977 FT_Vector tmp_result;
1979 line_desc_t *p_line = NULL;
1980 line_desc_t *p_prev = NULL;
1986 p_result->x = p_result->y = 0;
1987 tmp_result.x = tmp_result.y = 0;
1990 for( k = 0; k <= (uint32_t) i_len; k++ )
1992 if( ( k == (uint32_t) i_len ) ||
1994 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1996 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1998 /* End of the current style run */
1999 FT_Face p_face = NULL;
2002 /* Look for a match amongst our attachments first */
2003 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2007 char *psz_fontfile = NULL;
2009 psz_fontfile = FontConfig_Select( NULL,
2010 p_style->psz_fontname,
2014 if( psz_fontfile && ! *psz_fontfile )
2016 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2017 " so using default font", p_style->psz_fontname,
2018 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2019 (p_style->b_bold ? "(Bold)" :
2020 (p_style->b_italic ? "(Italic)" : ""))) );
2021 free( psz_fontfile );
2022 psz_fontfile = NULL;
2027 if( FT_New_Face( p_sys->p_library,
2028 psz_fontfile, i_idx, &p_face ) )
2030 free( psz_fontfile );
2031 free( pp_char_styles );
2032 #if defined(HAVE_FRIBIDI)
2035 free( pi_karaoke_bar );
2036 return VLC_EGENERIC;
2038 free( psz_fontfile );
2042 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2044 /* We've loaded a font face which is unhelpful for actually
2045 * rendering text - fallback to the default one.
2047 FT_Done_Face( p_face );
2051 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2052 ft_encoding_unicode ) ||
2053 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2054 p_style->i_font_size ) )
2056 if( p_face ) FT_Done_Face( p_face );
2057 free( pp_char_styles );
2058 #if defined(HAVE_FRIBIDI)
2061 free( pi_karaoke_bar );
2062 return VLC_EGENERIC;
2064 p_sys->i_use_kerning =
2065 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2068 uint32_t *psz_unicode = (uint32_t *)
2069 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2072 if( p_face ) FT_Done_Face( p_face );
2073 free( pp_char_styles );
2074 free( psz_unicode );
2075 #if defined(HAVE_FRIBIDI)
2078 free( pi_karaoke_bar );
2081 memcpy( psz_unicode, psz_text + i_prev,
2082 sizeof( uint32_t ) * ( k - i_prev ) );
2083 psz_unicode[ k - i_prev ] = 0;
2084 while( *psz_unicode )
2088 if( !(p_line = NewLine( i_len - i_prev)) )
2090 if( p_face ) FT_Done_Face( p_face );
2091 free( pp_char_styles );
2092 free( psz_unicode );
2093 #if defined(HAVE_FRIBIDI)
2096 free( pi_karaoke_bar );
2099 /* New Color mode only works in YUVA rendering mode --
2100 * (RGB mode has palette constraints on it). We therefore
2101 * need to populate the legacy colour fields also.
2103 p_line->b_new_color_mode = true;
2104 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2105 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2106 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2107 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2108 p_line->p_next = NULL;
2110 i_pen_y += tmp_result.y;
2114 if( p_prev ) p_prev->p_next = p_line;
2115 else *pp_lines = p_line;
2118 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2119 p_style->i_font_color, p_style->b_underline,
2123 p_style->i_karaoke_bg_color,
2124 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2125 &tmp_result ) != VLC_SUCCESS )
2127 if( p_face ) FT_Done_Face( p_face );
2128 free( pp_char_styles );
2129 free( psz_unicode );
2130 #if defined(HAVE_FRIBIDI)
2133 free( pi_karaoke_bar );
2134 return VLC_EGENERIC;
2139 p_result->x = __MAX( p_result->x, tmp_result.x );
2140 p_result->y += tmp_result.y;
2145 if( *psz_unicode == '\n')
2149 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2151 *c_ptr = *(c_ptr+1);
2156 free( psz_unicode );
2157 if( p_face ) FT_Done_Face( p_face );
2161 free( pp_char_styles );
2162 #if defined(HAVE_FRIBIDI)
2167 p_result->x = __MAX( p_result->x, tmp_result.x );
2168 p_result->y += tmp_result.y;
2171 if( pi_karaoke_bar )
2174 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2176 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2178 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2182 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2184 /* 100% BG colour will render faster if we
2185 * instead make it 100% FG colour, so leave
2186 * the ratio alone and copy the value across
2188 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2192 if( pi_karaoke_bar[ i ] & 0x80 )
2194 /* Swap Left and Right sides over for Right aligned
2195 * language text (eg. Arabic, Hebrew)
2197 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2199 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2200 p_line->p_bg_rgb[ k ] = i_tmp;
2202 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2205 /* Jump over the '\n' at the line-end */
2208 free( pi_karaoke_bar );
2214 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2215 subpicture_region_t *p_region_in )
2217 int rv = VLC_SUCCESS;
2218 stream_t *p_sub = NULL;
2219 xml_reader_t *p_xml_reader = NULL;
2221 if( !p_region_in || !p_region_in->psz_html )
2222 return VLC_EGENERIC;
2224 /* Reset the default fontsize in case screen metrics have changed */
2225 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2227 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2228 (uint8_t *) p_region_in->psz_html,
2229 strlen( p_region_in->psz_html ),
2233 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2234 if( p_filter->p_sys->p_xml )
2236 bool b_karaoke = false;
2238 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2241 /* Look for Root Node */
2242 if( xml_ReaderRead( p_xml_reader ) == 1 )
2244 char *psz_node = xml_ReaderName( p_xml_reader );
2246 if( !strcasecmp( "karaoke", psz_node ) )
2248 /* We're going to have to render the text a number
2249 * of times to show the progress marker on the text.
2251 var_SetBool( p_filter, "text-rerender", true );
2254 else if( !strcasecmp( "text", psz_node ) )
2260 /* Only text and karaoke tags are supported */
2261 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2262 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2263 p_xml_reader = NULL;
2275 uint32_t i_runs = 0;
2276 uint32_t i_k_runs = 0;
2277 uint32_t *pi_run_lengths = NULL;
2278 uint32_t *pi_k_run_lengths = NULL;
2279 uint32_t *pi_k_durations = NULL;
2280 ft_style_t **pp_styles = NULL;
2282 line_desc_t *p_lines = NULL;
2284 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2285 sizeof( uint32_t ) );
2290 rv = ProcessNodes( p_filter, p_xml_reader,
2291 p_region_in->p_style, psz_text, &i_len,
2292 &i_runs, &pi_run_lengths, &pp_styles,
2294 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2297 p_region_out->i_x = p_region_in->i_x;
2298 p_region_out->i_y = p_region_in->i_y;
2300 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2302 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2303 pi_run_lengths, pp_styles, &p_lines, &result,
2304 b_karaoke, i_k_runs, pi_k_run_lengths,
2308 for( k=0; k<i_runs; k++)
2309 DeleteStyle( pp_styles[k] );
2311 free( pi_run_lengths );
2314 /* Don't attempt to render text that couldn't be layed out
2317 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2319 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2321 Render( p_filter, p_region_out, p_lines,
2322 result.x, result.y );
2326 RenderYUVA( p_filter, p_region_out, p_lines,
2327 result.x, result.y );
2331 FreeLines( p_lines );
2333 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2336 stream_Delete( p_sub );
2342 static char* FontConfig_Select( FcConfig* priv, const char* family,
2343 bool b_bold, bool b_italic, int *i_idx )
2346 FcPattern *pat, *p_pat;
2350 pat = FcPatternCreate();
2351 if (!pat) return NULL;
2353 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2354 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2355 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2356 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2358 FcDefaultSubstitute( pat );
2360 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2362 FcPatternDestroy( pat );
2366 p_pat = FcFontMatch( priv, pat, &result );
2367 FcPatternDestroy( pat );
2368 if( !p_pat ) return NULL;
2370 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2371 || ( val_b != FcTrue ) )
2373 FcPatternDestroy( p_pat );
2376 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2381 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2383 FcPatternDestroy( p_pat );
2388 if( strcasecmp((const char*)val_s, family ) != 0 )
2389 msg_Warn( p_filter, "fontconfig: selected font family is not"
2390 "the requested one: '%s' != '%s'\n",
2391 (const char*)val_s, family );
2394 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2396 FcPatternDestroy( p_pat );
2400 FcPatternDestroy( p_pat );
2401 return strdup( (const char*)val_s );
2405 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2406 uint32_t **psz_text_out, uint32_t *pi_runs,
2407 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2408 ft_style_t *p_style )
2410 VLC_UNUSED(p_filter);
2411 VLC_UNUSED(psz_text_in);
2412 VLC_UNUSED(psz_text_out);
2413 VLC_UNUSED(pi_runs);
2414 VLC_UNUSED(ppi_run_lengths);
2415 VLC_UNUSED(ppp_styles);
2416 VLC_UNUSED(p_style);
2419 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2420 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2421 bool b_uline, bool b_through )
2424 VLC_UNUSED(p_fonts);
2426 VLC_UNUSED(b_italic);
2427 VLC_UNUSED(b_uline);
2428 VLC_UNUSED(b_through);
2433 static void FreeLine( line_desc_t *p_line )
2436 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2438 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2440 free( p_line->pp_glyphs );
2441 free( p_line->p_glyph_pos );
2442 free( p_line->p_fg_rgb );
2443 free( p_line->p_bg_rgb );
2444 free( p_line->p_fg_bg_ratio );
2445 free( p_line->pi_underline_offset );
2446 free( p_line->pi_underline_thickness );
2450 static void FreeLines( line_desc_t *p_lines )
2452 line_desc_t *p_line, *p_next;
2454 if( !p_lines ) return;
2456 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2458 p_next = p_line->p_next;
2463 static line_desc_t *NewLine( int i_count )
2465 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2467 if( !p_line ) return NULL;
2468 p_line->i_height = 0;
2469 p_line->i_width = 0;
2470 p_line->p_next = NULL;
2472 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2473 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2474 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2475 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2476 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2477 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2478 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2479 if( ( p_line->pp_glyphs == NULL ) ||
2480 ( p_line->p_glyph_pos == NULL ) ||
2481 ( p_line->p_fg_rgb == NULL ) ||
2482 ( p_line->p_bg_rgb == NULL ) ||
2483 ( p_line->p_fg_bg_ratio == NULL ) ||
2484 ( p_line->pi_underline_offset == NULL ) ||
2485 ( p_line->pi_underline_thickness == NULL ) )
2487 free( p_line->pi_underline_thickness );
2488 free( p_line->pi_underline_offset );
2489 free( p_line->p_fg_rgb );
2490 free( p_line->p_bg_rgb );
2491 free( p_line->p_fg_bg_ratio );
2492 free( p_line->p_glyph_pos );
2493 free( p_line->pp_glyphs );
2497 p_line->pp_glyphs[0] = NULL;
2498 p_line->b_new_color_mode = false;
2503 static int GetFontSize( filter_t *p_filter )
2505 filter_sys_t *p_sys = p_filter->p_sys;
2509 if( p_sys->i_default_font_size )
2511 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2512 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2514 i_size = p_sys->i_default_font_size;
2518 var_Get( p_filter, "freetype-rel-fontsize", &val );
2521 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2522 p_filter->p_sys->i_display_height =
2523 p_filter->fmt_out.video.i_height;
2528 msg_Warn( p_filter, "invalid fontsize, using 12" );
2529 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2530 i_size = 12 * val.i_int / 1000;
2537 static int SetFontSize( filter_t *p_filter, int i_size )
2539 filter_sys_t *p_sys = p_filter->p_sys;
2543 i_size = GetFontSize( p_filter );
2545 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2548 p_sys->i_font_size = i_size;
2550 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2552 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2553 return VLC_EGENERIC;
2559 static void YUVFromRGB( uint32_t i_argb,
2560 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2562 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2563 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2564 int i_blue = ( i_argb & 0x000000ff );
2566 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2567 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2568 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2569 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2570 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2571 -585 * i_blue + 4096 + 1048576) >> 13, 240);