1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
42 #include <vlc_dialog.h>
43 #include <vlc_memory.h>
48 #include <freetype/ftsynth.h>
49 #include FT_FREETYPE_H
51 #define FT_FLOOR(X) ((X & -64) >> 6)
52 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
54 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
59 #define FC_DEFAULT_FONT "Arial Black"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
66 #elif defined( HAVE_MAEMO )
67 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
68 #define FC_DEFAULT_FONT "Nokia Sans Bold"
70 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
71 #define FC_DEFAULT_FONT "Serif Bold"
74 #if defined(HAVE_FRIBIDI)
75 #include <fribidi/fribidi.h>
78 #ifdef HAVE_FONTCONFIG
79 #include <fontconfig/fontconfig.h>
81 #define DEFAULT_FONT FC_DEFAULT_FONT
86 /*****************************************************************************
88 *****************************************************************************/
89 static int Create ( vlc_object_t * );
90 static void Destroy( vlc_object_t * );
92 #define FONT_TEXT N_("Font")
94 #ifdef HAVE_FONTCONFIG
95 #define FONT_LONGTEXT N_("Font family for the font you want to use")
97 #define FONT_LONGTEXT N_("Font file for the font you want to use")
100 #define FONTSIZE_TEXT N_("Font size in pixels")
101 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
102 "that will be rendered on the video. " \
103 "If set to something different than 0 this option will override the " \
104 "relative font size." )
105 #define OPACITY_TEXT N_("Opacity")
106 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
107 "text that will be rendered on the video. 0 = transparent, " \
108 "255 = totally opaque. " )
109 #define COLOR_TEXT N_("Text default color")
110 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
111 "the video. This must be an hexadecimal (like HTML colors). The first two "\
112 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
113 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
114 #define FONTSIZER_TEXT N_("Relative font size")
115 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
116 "fonts that will be rendered on the video. If absolute font size is set, "\
117 "relative size will be overridden." )
119 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
120 static const char *const ppsz_sizes_text[] = {
121 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
122 #define YUVP_TEXT N_("Use YUVP renderer")
123 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
124 "This option is only needed if you want to encode into DVB subtitles" )
125 #define EFFECT_TEXT N_("Font Effect")
126 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
127 "text to improve its readability." )
129 #define EFFECT_BACKGROUND 1
130 #define EFFECT_OUTLINE 2
131 #define EFFECT_OUTLINE_FAT 3
133 static int const pi_effects[] = { 1, 2, 3 };
134 static const char *const ppsz_effects_text[] = {
135 N_("Background"),N_("Outline"), N_("Fat Outline") };
136 static const int pi_color_values[] = {
137 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
138 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
139 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
141 static const char *const ppsz_color_descriptions[] = {
142 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
143 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
144 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
147 set_shortname( N_("Text renderer"))
148 set_description( N_("Freetype2 font renderer") )
149 set_category( CAT_VIDEO )
150 set_subcategory( SUBCAT_VIDEO_SUBPIC )
152 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
155 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
156 FONTSIZE_LONGTEXT, true )
158 /* opacity valid on 0..255, with default 255 = fully opaque */
159 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
160 OPACITY_TEXT, OPACITY_LONGTEXT, true )
162 /* hook to the color values list, with default 0x00ffffff = white */
163 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
164 COLOR_LONGTEXT, false )
165 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
167 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
168 FONTSIZER_LONGTEXT, false )
169 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
170 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
171 EFFECT_LONGTEXT, false )
172 change_integer_list( pi_effects, ppsz_effects_text, NULL )
174 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
175 YUVP_LONGTEXT, true )
176 set_capability( "text renderer", 100 )
177 add_shortcut( "text" )
178 set_callbacks( Create, Destroy )
183 /*****************************************************************************
185 *****************************************************************************/
187 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
188 static int RenderText( filter_t *, subpicture_region_t *,
189 subpicture_region_t * );
190 #ifdef HAVE_FONTCONFIG
191 static int RenderHtml( filter_t *, subpicture_region_t *,
192 subpicture_region_t * );
193 static char *FontConfig_Select( FcConfig *, const char *,
198 static int LoadFontsFromAttachments( filter_t *p_filter );
200 static int GetFontSize( filter_t *p_filter );
201 static int SetFontSize( filter_t *, int );
202 static void YUVFromRGB( uint32_t i_argb,
203 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
205 typedef struct line_desc_t line_desc_t;
208 /** NULL-terminated list of glyphs making the string */
209 FT_BitmapGlyph *pp_glyphs;
210 /** list of relative positions for the glyphs */
211 FT_Vector *p_glyph_pos;
212 /** list of RGB information for styled text
213 * -- if the rendering mode supports it (RenderYUVA) and
214 * b_new_color_mode is set, then it becomes possible to
215 * have multicoloured text within the subtitles. */
218 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
219 bool b_new_color_mode;
220 /** underline information -- only supplied if text should be underlined */
221 int *pi_underline_offset;
222 uint16_t *pi_underline_thickness;
226 int i_red, i_green, i_blue;
231 static line_desc_t *NewLine( int );
236 uint32_t i_font_color; /* ARGB */
237 uint32_t i_karaoke_bg_color; /* ARGB */
245 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
246 static void FreeLines( line_desc_t * );
247 static void FreeLine( line_desc_t * );
249 /*****************************************************************************
250 * filter_sys_t: freetype local data
251 *****************************************************************************
252 * This structure is part of the video output thread descriptor.
253 * It describes the freetype specific properties of an output thread.
254 *****************************************************************************/
257 FT_Library p_library; /* handle to library */
258 FT_Face p_face; /* handle to face object */
260 uint8_t i_font_opacity;
265 int i_default_font_size;
266 int i_display_height;
267 #ifdef HAVE_FONTCONFIG
268 char* psz_fontfamily;
272 input_attachment_t **pp_font_attachments;
273 int i_font_attachments;
277 #define UCHAR uint32_t
278 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
279 #define TR_FONT_STYLE_PTR ft_style_t *
281 #include "text_renderer.h"
283 /*****************************************************************************
284 * Create: allocates osd-text video thread output method
285 *****************************************************************************
286 * This function allocates and initializes a Clone vout method.
287 *****************************************************************************/
288 static int Create( vlc_object_t *p_this )
290 filter_t *p_filter = (filter_t *)p_this;
292 char *psz_fontfile=NULL;
293 char *psz_fontfamily=NULL;
294 int i_error,fontindex;
296 #ifdef HAVE_FONTCONFIG
297 FcPattern *fontpattern = NULL, *fontmatch = NULL;
298 /* Initialise result to Match, as fontconfig doesnt
299 * really set this other than some error-cases */
300 FcResult fontresult = FcResultMatch;
304 /* Allocate structure */
305 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
308 #ifdef HAVE_FONTCONFIG
309 p_sys->psz_fontfamily = NULL;
313 p_sys->p_library = 0;
314 p_sys->i_font_size = 0;
315 p_sys->i_display_height = 0;
317 var_Create( p_filter, "freetype-rel-fontsize",
318 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
320 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
321 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
322 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
323 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
324 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
325 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
326 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
329 if( !psz_fontfamily || !*psz_fontfamily )
331 free( psz_fontfamily );
332 #ifdef HAVE_FONTCONFIG
333 psz_fontfamily=strdup( DEFAULT_FONT );
335 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
336 if( !psz_fontfamily )
339 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
340 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
342 strcpy( psz_fontfamily, DEFAULT_FONT );
344 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
348 #ifdef HAVE_FONTCONFIG
349 msg_Dbg( p_filter, "Building font databases.");
354 dialog_progress_bar_t *p_dialog = NULL;
355 FcConfig *fcConfig = FcInitLoadConfig();
357 p_dialog = dialog_ProgressCreate( p_filter,
358 _("Building font cache"),
359 _("Please wait while your font cache is rebuilt.\n"
360 "This should take less than a few minutes."), NULL );
363 dialog_ProgressSet( p_dialog, NULL, 0.5 );
365 FcConfigBuildFonts( fcConfig );
367 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
371 dialog_ProgressSet( p_dialog, NULL, 1.0 );
372 dialog_ProgressDestroy( p_dialog );
376 /* Lets find some fontfile from freetype-font variable family */
378 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
381 fontpattern = FcPatternCreate();
384 msg_Err( p_filter, "Creating fontpattern failed");
388 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
389 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
390 free( psz_fontsize );
392 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
394 msg_Err( p_filter, "FontSubstitute failed");
397 FcDefaultSubstitute( fontpattern );
399 /* testing fontresult here doesn't do any good really, but maybe it will
400 * in future as fontconfig code doesn't set it in all cases and just
401 * returns NULL or doesn't set to to Match on all Match cases.*/
402 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
403 if( !fontmatch || fontresult == FcResultNoMatch )
405 msg_Err( p_filter, "Fontmatching failed");
409 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
410 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
413 msg_Err( p_filter, "Failed to get fontfile");
417 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
418 psz_fontfile ? psz_fontfile : "(null)" );
419 p_sys->psz_fontfamily = strdup( psz_fontfamily );
423 psz_fontfile = psz_fontfamily;
427 i_error = FT_Init_FreeType( &p_sys->p_library );
430 msg_Err( p_filter, "couldn't initialize freetype" );
434 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
435 fontindex, &p_sys->p_face );
437 if( i_error == FT_Err_Unknown_File_Format )
439 msg_Err( p_filter, "file %s have unknown format",
440 psz_fontfile ? psz_fontfile : "(null)" );
445 msg_Err( p_filter, "failed to load font file %s",
446 psz_fontfile ? psz_fontfile : "(null)" );
450 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
453 msg_Err( p_filter, "font has no unicode translation table" );
457 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
459 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
462 p_sys->pp_font_attachments = NULL;
463 p_sys->i_font_attachments = 0;
465 p_filter->pf_render_text = RenderText;
466 #ifdef HAVE_FONTCONFIG
467 p_filter->pf_render_html = RenderHtml;
468 FcPatternDestroy( fontmatch );
469 FcPatternDestroy( fontpattern );
471 p_filter->pf_render_html = NULL;
474 free( psz_fontfamily );
475 LoadFontsFromAttachments( p_filter );
480 #ifdef HAVE_FONTCONFIG
481 if( fontmatch ) FcPatternDestroy( fontmatch );
482 if( fontpattern ) FcPatternDestroy( fontpattern );
487 dialog_ProgressDestroy( p_dialog );
490 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
491 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
492 free( psz_fontfamily );
497 /*****************************************************************************
498 * Destroy: destroy Clone video thread output method
499 *****************************************************************************
500 * Clean up all data and library connections
501 *****************************************************************************/
502 static void Destroy( vlc_object_t *p_this )
504 filter_t *p_filter = (filter_t *)p_this;
505 filter_sys_t *p_sys = p_filter->p_sys;
507 if( p_sys->pp_font_attachments )
511 for( k = 0; k < p_sys->i_font_attachments; k++ )
512 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
514 free( p_sys->pp_font_attachments );
517 #ifdef HAVE_FONTCONFIG
518 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
519 free( p_sys->psz_fontfamily );
522 /* FcFini asserts calling the subfunction FcCacheFini()
523 * even if no other library functions have been made since FcInit(),
524 * so don't call it. */
526 FT_Done_Face( p_sys->p_face );
527 FT_Done_FreeType( p_sys->p_library );
531 /*****************************************************************************
532 * Make any TTF/OTF fonts present in the attachments of the media file
533 * and store them for later use by the FreeType Engine
534 *****************************************************************************/
535 static int LoadFontsFromAttachments( filter_t *p_filter )
537 filter_sys_t *p_sys = p_filter->p_sys;
538 input_thread_t *p_input;
539 input_attachment_t **pp_attachments;
540 int i_attachments_cnt;
542 int rv = VLC_SUCCESS;
544 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
548 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
550 vlc_object_release(p_input);
554 p_sys->i_font_attachments = 0;
555 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
556 if(! p_sys->pp_font_attachments )
559 for( k = 0; k < i_attachments_cnt; k++ )
561 input_attachment_t *p_attach = pp_attachments[k];
563 if( p_sys->pp_font_attachments )
565 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
566 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
567 ( p_attach->i_data > 0 ) &&
568 ( p_attach->p_data != NULL ) )
570 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
574 vlc_input_attachment_Delete( p_attach );
579 vlc_input_attachment_Delete( p_attach );
582 free( pp_attachments );
584 vlc_object_release(p_input);
589 /*****************************************************************************
590 * Render: place string in picture
591 *****************************************************************************
592 * This function merges the previously rendered freetype glyphs into a picture
593 *****************************************************************************/
594 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
595 line_desc_t *p_line, int i_width, int i_height )
597 VLC_UNUSED(p_filter);
598 static const uint8_t pi_gamma[16] =
599 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
600 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
604 int i, x, y, i_pitch;
605 uint8_t i_y; /* YUV values, derived from incoming RGB */
608 /* Create a new subpicture region */
609 memset( &fmt, 0, sizeof(video_format_t) );
610 fmt.i_chroma = VLC_CODEC_YUVP;
611 fmt.i_width = fmt.i_visible_width = i_width + 4;
612 fmt.i_height = fmt.i_visible_height = i_height + 4;
613 if( p_region->fmt.i_visible_width > 0 )
614 fmt.i_visible_width = p_region->fmt.i_visible_width;
615 if( p_region->fmt.i_visible_height > 0 )
616 fmt.i_visible_height = p_region->fmt.i_visible_height;
617 fmt.i_x_offset = fmt.i_y_offset = 0;
619 assert( !p_region->p_picture );
620 p_region->p_picture = picture_NewFromFormat( &fmt );
621 if( !p_region->p_picture )
623 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
626 /* Calculate text color components */
627 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
628 25 * p_line->i_blue + 128) >> 8) + 16;
629 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
630 112 * p_line->i_blue + 128) >> 8) + 128;
631 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
632 18 * p_line->i_blue + 128) >> 8) + 128;
635 fmt.p_palette->i_entries = 16;
636 for( i = 0; i < 8; i++ )
638 fmt.p_palette->palette[i][0] = 0;
639 fmt.p_palette->palette[i][1] = 0x80;
640 fmt.p_palette->palette[i][2] = 0x80;
641 fmt.p_palette->palette[i][3] = pi_gamma[i];
642 fmt.p_palette->palette[i][3] =
643 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
645 for( i = 8; i < fmt.p_palette->i_entries; i++ )
647 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
648 fmt.p_palette->palette[i][1] = i_u;
649 fmt.p_palette->palette[i][2] = i_v;
650 fmt.p_palette->palette[i][3] = pi_gamma[i];
651 fmt.p_palette->palette[i][3] =
652 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
655 p_dst = p_region->p_picture->Y_PIXELS;
656 i_pitch = p_region->p_picture->Y_PITCH;
658 /* Initialize the region pixels */
659 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
661 for( ; p_line != NULL; p_line = p_line->p_next )
663 int i_glyph_tmax = 0;
664 int i_bitmap_offset, i_offset, i_align_offset = 0;
665 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
667 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
668 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
671 if( p_line->i_width < i_width )
673 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
675 i_align_offset = i_width - p_line->i_width;
677 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
679 i_align_offset = ( i_width - p_line->i_width ) / 2;
683 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
685 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
687 i_offset = ( p_line->p_glyph_pos[ i ].y +
688 i_glyph_tmax - p_glyph->top + 2 ) *
689 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
692 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
694 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
696 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
698 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
705 /* Outlining (find something better than nearest neighbour filtering ?) */
708 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
709 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
710 uint8_t left, current;
712 for( y = 1; y < (int)fmt.i_height - 1; y++ )
714 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
715 p_dst += p_region->p_picture->Y_PITCH;
718 for( x = 1; x < (int)fmt.i_width - 1; x++ )
721 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
722 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;
726 memset( p_top, 0, fmt.i_width );
732 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
733 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
734 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
735 int i_glyph_tmax, int i_align_offset,
736 uint8_t i_y, uint8_t i_u, uint8_t i_v,
737 subpicture_region_t *p_region)
741 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
743 p_dst_y = p_region->p_picture->Y_PIXELS;
744 p_dst_u = p_region->p_picture->U_PIXELS;
745 p_dst_v = p_region->p_picture->V_PIXELS;
746 p_dst_a = p_region->p_picture->A_PIXELS;
747 i_pitch = p_region->p_picture->A_PITCH;
749 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
750 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
752 for( y = 0; y < i_line_thickness; y++ )
754 int i_extra = p_this_glyph->bitmap.width;
758 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
759 (p_this_glyph_pos->x + p_this_glyph->left);
761 for( x = 0; x < i_extra; x++ )
765 /* break the underline around the tails of any glyphs which cross it */
766 /* Strikethrough doesn't get broken */
767 for( z = x - i_line_thickness;
768 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
771 if( p_next_glyph && ( z >= i_extra ) )
773 int i_row = i_line_offset + p_next_glyph->top + y;
775 if( ( p_next_glyph->bitmap.rows > i_row ) &&
776 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
781 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
783 int i_row = i_line_offset + p_this_glyph->top + y;
785 if( ( p_this_glyph->bitmap.rows > i_row ) &&
786 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
795 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
796 p_dst_u[i_offset+x] = i_u;
797 p_dst_v[i_offset+x] = i_v;
798 p_dst_a[i_offset+x] = 255;
805 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
807 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
808 int i_pitch = p_region->p_picture->A_PITCH;
811 for( ; p_line != NULL; p_line = p_line->p_next )
813 int i_glyph_tmax=0, i = 0;
814 int i_bitmap_offset, i_offset, i_align_offset = 0;
815 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
817 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
818 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
821 if( p_line->i_width < i_width )
823 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
825 i_align_offset = i_width - p_line->i_width;
827 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
829 i_align_offset = ( i_width - p_line->i_width ) / 2;
833 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
835 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
837 i_offset = ( p_line->p_glyph_pos[ i ].y +
838 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
839 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
840 i_align_offset +xoffset;
842 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
844 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
846 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
847 if( p_dst[i_offset+x] <
848 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
850 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
859 /*****************************************************************************
860 * Render: place string in picture
861 *****************************************************************************
862 * This function merges the previously rendered freetype glyphs into a picture
863 *****************************************************************************/
864 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
865 line_desc_t *p_line, int i_width, int i_height )
867 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
869 int i, x, y, i_pitch, i_alpha;
870 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
872 if( i_width == 0 || i_height == 0 )
875 /* Create a new subpicture region */
876 memset( &fmt, 0, sizeof(video_format_t) );
877 fmt.i_chroma = VLC_CODEC_YUVA;
878 fmt.i_width = fmt.i_visible_width = i_width + 6;
879 fmt.i_height = fmt.i_visible_height = i_height + 6;
880 if( p_region->fmt.i_visible_width > 0 )
881 fmt.i_visible_width = p_region->fmt.i_visible_width;
882 if( p_region->fmt.i_visible_height > 0 )
883 fmt.i_visible_height = p_region->fmt.i_visible_height;
884 fmt.i_x_offset = fmt.i_y_offset = 0;
886 p_region->p_picture = picture_NewFromFormat( &fmt );
887 if( !p_region->p_picture )
891 /* Calculate text color components */
892 YUVFromRGB( (p_line->i_red << 16) |
893 (p_line->i_green << 8) |
896 i_alpha = p_line->i_alpha;
898 p_dst_y = p_region->p_picture->Y_PIXELS;
899 p_dst_u = p_region->p_picture->U_PIXELS;
900 p_dst_v = p_region->p_picture->V_PIXELS;
901 p_dst_a = p_region->p_picture->A_PIXELS;
902 i_pitch = p_region->p_picture->A_PITCH;
904 /* Initialize the region pixels */
905 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
907 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
908 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
909 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
910 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
914 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
915 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
916 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
917 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
919 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
920 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
922 DrawBlack( p_line, i_width, p_region, 0, 0);
923 DrawBlack( p_line, i_width, p_region, -1, 0);
924 DrawBlack( p_line, i_width, p_region, 0, -1);
925 DrawBlack( p_line, i_width, p_region, 1, 0);
926 DrawBlack( p_line, i_width, p_region, 0, 1);
929 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
931 DrawBlack( p_line, i_width, p_region, -1, -1);
932 DrawBlack( p_line, i_width, p_region, -1, 1);
933 DrawBlack( p_line, i_width, p_region, 1, -1);
934 DrawBlack( p_line, i_width, p_region, 1, 1);
936 DrawBlack( p_line, i_width, p_region, -2, 0);
937 DrawBlack( p_line, i_width, p_region, 0, -2);
938 DrawBlack( p_line, i_width, p_region, 2, 0);
939 DrawBlack( p_line, i_width, p_region, 0, 2);
941 DrawBlack( p_line, i_width, p_region, -2, -2);
942 DrawBlack( p_line, i_width, p_region, -2, 2);
943 DrawBlack( p_line, i_width, p_region, 2, -2);
944 DrawBlack( p_line, i_width, p_region, 2, 2);
946 DrawBlack( p_line, i_width, p_region, -3, 0);
947 DrawBlack( p_line, i_width, p_region, 0, -3);
948 DrawBlack( p_line, i_width, p_region, 3, 0);
949 DrawBlack( p_line, i_width, p_region, 0, 3);
952 for( ; p_line != NULL; p_line = p_line->p_next )
954 int i_glyph_tmax = 0;
955 int i_bitmap_offset, i_offset, i_align_offset = 0;
956 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
958 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
959 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
962 if( p_line->i_width < i_width )
964 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
966 i_align_offset = i_width - p_line->i_width;
968 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
970 i_align_offset = ( i_width - p_line->i_width ) / 2;
974 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
976 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
978 i_offset = ( p_line->p_glyph_pos[ i ].y +
979 i_glyph_tmax - p_glyph->top + 3 ) *
980 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
983 if( p_line->b_new_color_mode )
985 /* Every glyph can (and in fact must) have its own color */
986 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
989 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
991 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
993 uint8_t i_y_local = i_y;
994 uint8_t i_u_local = i_u;
995 uint8_t i_v_local = i_v;
997 if( p_line->p_fg_bg_ratio != 0x00 )
999 int i_split = p_glyph->bitmap.width *
1000 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1004 YUVFromRGB( p_line->p_bg_rgb[ i ],
1005 &i_y_local, &i_u_local, &i_v_local );
1009 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1011 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1012 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1014 p_dst_u[i_offset+x] = i_u;
1015 p_dst_v[i_offset+x] = i_v;
1017 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1018 p_dst_a[i_offset+x] = 0xff;
1021 i_offset += i_pitch;
1024 if( p_line->pi_underline_thickness[ i ] )
1026 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1027 p_line->pi_underline_offset[ i ],
1028 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1029 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1030 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1031 i_glyph_tmax, i_align_offset,
1038 /* Apply the alpha setting */
1039 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1040 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1046 * This function renders a text subpicture region into another one.
1047 * It also calculates the size needed for this string, and renders the
1048 * needed glyphs into memory. It is used as pf_add_string callback in
1049 * the vout method by this module
1051 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1052 subpicture_region_t *p_region_in )
1054 filter_sys_t *p_sys = p_filter->p_sys;
1055 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1056 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1057 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1058 int i_string_length;
1060 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1061 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1071 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1072 psz_string = p_region_in->psz_text;
1073 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1075 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1076 i_scale = val.i_int;
1078 if( p_region_in->p_style )
1080 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1081 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1082 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1086 i_font_color = p_sys->i_font_color;
1087 i_font_alpha = 255 - p_sys->i_font_opacity;
1088 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1091 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1092 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1093 SetFontSize( p_filter, i_font_size );
1095 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1096 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1097 i_blue = i_font_color & 0x000000FF;
1099 result.x = result.y = 0;
1100 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1102 psz_unicode = psz_unicode_orig =
1103 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1104 if( psz_unicode == NULL )
1106 #if defined(WORDS_BIGENDIAN)
1107 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1109 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1111 if( iconv_handle == (vlc_iconv_t)-1 )
1113 msg_Warn( p_filter, "unable to do conversion" );
1119 const char *p_in_buffer = psz_string;
1120 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1121 i_in_bytes = strlen( psz_string );
1122 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1123 i_out_bytes_left = i_out_bytes;
1124 p_out_buffer = (char *)psz_unicode;
1125 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1127 &p_out_buffer, &i_out_bytes_left );
1129 vlc_iconv_close( iconv_handle );
1133 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1134 "bytes left %u", (unsigned)i_in_bytes );
1137 *(uint32_t*)p_out_buffer = 0;
1138 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1141 #if defined(HAVE_FRIBIDI)
1143 uint32_t *p_fribidi_string;
1144 int32_t start_pos, pos = 0;
1146 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1147 if( !p_fribidi_string )
1150 /* Do bidi conversion line-by-line */
1151 while( pos < i_string_length )
1153 while( pos < i_string_length )
1155 i_char = psz_unicode[pos];
1156 if (i_char != '\r' && i_char != '\n')
1158 p_fribidi_string[pos] = i_char;
1162 while( pos < i_string_length )
1164 i_char = psz_unicode[pos];
1165 if (i_char == '\r' || i_char == '\n')
1169 if (pos > start_pos)
1171 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1172 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1175 (FriBidiChar*)p_fribidi_string + start_pos,
1180 free( psz_unicode_orig );
1181 psz_unicode = psz_unicode_orig = p_fribidi_string;
1182 p_fribidi_string[ i_string_length ] = 0;
1186 /* Calculate relative glyph positions and a bounding box for the
1188 if( !(p_line = NewLine( strlen( psz_string ))) )
1191 i_pen_x = i_pen_y = 0;
1193 psz_line_start = psz_unicode;
1195 #define face p_sys->p_face
1196 #define glyph face->glyph
1198 while( *psz_unicode )
1200 i_char = *psz_unicode++;
1201 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1206 if( i_char == '\n' )
1208 psz_line_start = psz_unicode;
1209 if( !(p_next = NewLine( strlen( psz_string ))) )
1211 p_line->p_next = p_next;
1212 p_line->i_width = line.xMax;
1213 p_line->i_height = face->size->metrics.height >> 6;
1214 p_line->pp_glyphs[ i ] = NULL;
1215 p_line->i_alpha = i_font_alpha;
1216 p_line->i_red = i_red;
1217 p_line->i_green = i_green;
1218 p_line->i_blue = i_blue;
1221 result.x = __MAX( result.x, line.xMax );
1222 result.y += face->size->metrics.height >> 6;
1225 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1226 i_pen_y += face->size->metrics.height >> 6;
1228 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1233 i_glyph_index = FT_Get_Char_Index( face, i_char );
1234 if( p_sys->i_use_kerning && i_glyph_index
1238 FT_Get_Kerning( face, i_previous, i_glyph_index,
1239 ft_kerning_default, &delta );
1240 i_pen_x += delta.x >> 6;
1243 p_line->p_glyph_pos[ i ].x = i_pen_x;
1244 p_line->p_glyph_pos[ i ].y = i_pen_y;
1245 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1248 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1252 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1255 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1259 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1260 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1263 FT_Done_Glyph( tmp_glyph );
1266 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1269 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1270 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1271 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1273 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1274 p_line->pp_glyphs[ i ] = NULL;
1276 p_line = NewLine( strlen( psz_string ));
1277 if( p_prev ) p_prev->p_next = p_line;
1278 else p_lines = p_line;
1280 uint32_t *psz_unicode_saved = psz_unicode;
1281 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1285 if( psz_unicode == psz_line_start )
1286 { /* try harder to break that line */
1287 psz_unicode = psz_unicode_saved;
1288 while( psz_unicode > psz_line_start &&
1289 *psz_unicode != '_' && *psz_unicode != '/' &&
1290 *psz_unicode != '\\' && *psz_unicode != '.' )
1295 if( psz_unicode == psz_line_start )
1297 msg_Warn( p_filter, "unbreakable string" );
1302 *psz_unicode = '\n';
1304 psz_unicode = psz_line_start;
1307 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1310 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1311 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1313 i_previous = i_glyph_index;
1314 i_pen_x += glyph->advance.x >> 6;
1318 p_line->i_width = line.xMax;
1319 p_line->i_height = face->size->metrics.height >> 6;
1320 p_line->pp_glyphs[ i ] = NULL;
1321 p_line->i_alpha = i_font_alpha;
1322 p_line->i_red = i_red;
1323 p_line->i_green = i_green;
1324 p_line->i_blue = i_blue;
1325 result.x = __MAX( result.x, line.xMax );
1326 result.y += line.yMax - line.yMin;
1331 p_region_out->i_x = p_region_in->i_x;
1332 p_region_out->i_y = p_region_in->i_y;
1334 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1335 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1337 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1339 free( psz_unicode_orig );
1340 FreeLines( p_lines );
1344 free( psz_unicode_orig );
1345 FreeLines( p_lines );
1346 return VLC_EGENERIC;
1349 #ifdef HAVE_FONTCONFIG
1350 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1351 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1352 bool b_italic, bool b_uline, bool b_through )
1354 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1358 p_style->i_font_size = i_font_size;
1359 p_style->i_font_color = i_font_color;
1360 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1361 p_style->b_italic = b_italic;
1362 p_style->b_bold = b_bold;
1363 p_style->b_underline = b_uline;
1364 p_style->b_through = b_through;
1366 p_style->psz_fontname = strdup( psz_fontname );
1371 static void DeleteStyle( ft_style_t *p_style )
1375 free( p_style->psz_fontname );
1380 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1387 if(( s1->i_font_size == s2->i_font_size ) &&
1388 ( s1->i_font_color == s2->i_font_color ) &&
1389 ( s1->b_italic == s2->b_italic ) &&
1390 ( s1->b_through == s2->b_through ) &&
1391 ( s1->b_bold == s2->b_bold ) &&
1392 ( s1->b_underline == s2->b_underline ) &&
1393 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1400 static void IconvText( filter_t *p_filter, const char *psz_string,
1401 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1403 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1405 /* If memory hasn't been allocated for our output string, allocate it here
1406 * - the calling function must now be responsible for freeing it.
1408 if( !*ppsz_unicode )
1409 *ppsz_unicode = (uint32_t *)
1410 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1412 /* We don't need to handle a NULL pointer in *ppsz_unicode
1413 * if we are instead testing for a non NULL value like we are here */
1417 #if defined(WORDS_BIGENDIAN)
1418 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1420 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1422 if( iconv_handle != (vlc_iconv_t)-1 )
1424 char *p_in_buffer, *p_out_buffer;
1425 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1426 i_in_bytes = strlen( psz_string );
1427 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1428 i_out_bytes_left = i_out_bytes;
1429 p_in_buffer = (char *) psz_string;
1430 p_out_buffer = (char *) *ppsz_unicode;
1431 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1432 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1434 vlc_iconv_close( iconv_handle );
1438 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1439 "bytes left %u", (unsigned)i_in_bytes );
1443 *(uint32_t*)p_out_buffer = 0;
1445 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1450 msg_Warn( p_filter, "unable to do conversion" );
1455 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1456 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1457 bool b_uline, bool b_through )
1459 ft_style_t *p_style = NULL;
1461 char *psz_fontname = NULL;
1462 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1463 uint32_t i_karaoke_bg_color = i_font_color;
1464 int i_font_size = p_sys->i_font_size;
1466 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1467 &i_font_color, &i_karaoke_bg_color ))
1469 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1470 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1475 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1476 bool b_uline, bool b_through, bool b_bold,
1477 bool b_italic, int i_karaoke_bgcolor,
1478 line_desc_t *p_line, uint32_t *psz_unicode,
1479 int *pi_pen_x, int i_pen_y, int *pi_start,
1480 FT_Vector *p_result )
1485 bool b_first_on_line = true;
1488 int i_pen_x_start = *pi_pen_x;
1490 uint32_t *psz_unicode_start = psz_unicode;
1492 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1494 /* Account for part of line already in position */
1495 for( i=0; i<*pi_start; i++ )
1499 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1500 ft_glyph_bbox_pixels, &glyph_size );
1502 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1503 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1504 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1505 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1511 b_first_on_line = false;
1513 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1519 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1520 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1524 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1525 ft_kerning_default, &delta );
1526 *pi_pen_x += delta.x >> 6;
1528 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1529 p_line->p_glyph_pos[ i ].y = i_pen_y;
1531 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1535 "unable to render text FT_Load_Glyph returned %d", i_error );
1536 p_line->pp_glyphs[ i ] = NULL;
1537 return VLC_EGENERIC;
1540 /* Do synthetic styling now that Freetype supports it;
1541 * ie. if the font we have loaded is NOT already in the
1542 * style that the tags want, then switch it on; if they
1543 * are then don't. */
1544 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1545 FT_GlyphSlot_Embolden( p_face->glyph );
1546 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1547 FT_GlyphSlot_Oblique( p_face->glyph );
1549 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1553 "unable to render text FT_Get_Glyph returned %d", i_error );
1554 p_line->pp_glyphs[ i ] = NULL;
1555 return VLC_EGENERIC;
1557 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1558 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1561 FT_Done_Glyph( tmp_glyph );
1564 if( b_uline || b_through )
1566 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1567 p_face->size->metrics.y_scale));
1568 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1569 p_face->size->metrics.y_scale));
1571 p_line->pi_underline_offset[ i ] =
1572 ( aOffset < 0 ) ? -aOffset : aOffset;
1573 p_line->pi_underline_thickness[ i ] =
1574 ( aSize < 0 ) ? -aSize : aSize;
1577 /* Move the baseline to make it strikethrough instead of
1578 * underline. That means that strikethrough takes precedence
1580 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1581 p_face->size->metrics.y_scale));
1583 p_line->pi_underline_offset[ i ] -=
1584 ( aDescent < 0 ) ? -aDescent : aDescent;
1588 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1589 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1590 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1591 p_line->p_fg_bg_ratio[ i ] = 0x00;
1593 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1594 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1595 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1597 for( ; i >= *pi_start; i-- )
1598 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1601 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1605 if( psz_unicode == psz_unicode_start )
1607 if( b_first_on_line )
1609 msg_Warn( p_filter, "unbreakable string" );
1610 p_line->pp_glyphs[ i ] = NULL;
1611 return VLC_EGENERIC;
1613 *pi_pen_x = i_pen_x_start;
1615 p_line->i_width = line.xMax;
1616 p_line->i_height = __MAX( p_line->i_height,
1617 p_face->size->metrics.height >> 6 );
1618 p_line->pp_glyphs[ i ] = NULL;
1620 p_result->x = __MAX( p_result->x, line.xMax );
1621 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1622 i_yMax - i_yMin ) );
1627 *psz_unicode = '\n';
1629 psz_unicode = psz_unicode_start;
1630 *pi_pen_x = i_pen_x_start;
1638 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1639 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1641 i_previous = i_glyph_index;
1642 *pi_pen_x += p_face->glyph->advance.x >> 6;
1645 p_line->i_width = line.xMax;
1646 p_line->i_height = __MAX( p_line->i_height,
1647 p_face->size->metrics.height >> 6 );
1648 p_line->pp_glyphs[ i ] = NULL;
1650 p_result->x = __MAX( p_result->x, line.xMax );
1651 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1652 line.yMax - line.yMin ) );
1656 /* Get rid of any text processed - if necessary repositioning
1657 * at the start of a new line of text
1661 *psz_unicode_start = '\0';
1663 else if( psz_unicode > psz_unicode_start )
1665 for( i=0; psz_unicode[ i ]; i++ )
1666 psz_unicode_start[ i ] = psz_unicode[ i ];
1667 psz_unicode_start[ i ] = '\0';
1673 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1674 uint32_t **psz_text_out, uint32_t *pi_runs,
1675 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1676 ft_style_t *p_style )
1678 uint32_t i_string_length = 0;
1680 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1681 *psz_text_out += i_string_length;
1683 if( ppp_styles && ppi_run_lengths )
1687 /* XXX this logic looks somewhat broken */
1691 *ppp_styles = realloc_or_free( *ppp_styles,
1692 *pi_runs * sizeof( ft_style_t * ) );
1694 else if( *pi_runs == 1 )
1696 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1699 /* We have just malloc'ed this memory successfully -
1700 * *pi_runs HAS to be within the memory area of *ppp_styles */
1703 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1707 /* XXX more iffy logic */
1709 if( *ppi_run_lengths )
1711 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1712 *pi_runs * sizeof( uint32_t ) );
1714 else if( *pi_runs == 1 )
1716 *ppi_run_lengths = (uint32_t *)
1717 malloc( *pi_runs * sizeof( uint32_t ) );
1720 /* same remarks here */
1721 if( *ppi_run_lengths )
1723 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1726 /* If we couldn't use the p_style argument due to memory allocation
1727 * problems above, release it here.
1729 if( p_style ) DeleteStyle( p_style );
1732 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1736 for( k=0; k < p_sys->i_font_attachments; k++ )
1738 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1740 FT_Face p_face = NULL;
1742 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1750 bool match = !strcasecmp( p_face->family_name,
1751 p_style->psz_fontname );
1753 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1754 match = match && p_style->b_bold;
1756 match = match && !p_style->b_bold;
1758 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1759 match = match && p_style->b_italic;
1761 match = match && !p_style->b_italic;
1769 FT_Done_Face( p_face );
1774 return VLC_EGENERIC;
1777 static int ProcessLines( filter_t *p_filter,
1782 uint32_t *pi_run_lengths,
1783 ft_style_t **pp_styles,
1784 line_desc_t **pp_lines,
1786 FT_Vector *p_result,
1790 uint32_t *pi_k_run_lengths,
1791 uint32_t *pi_k_durations )
1793 filter_sys_t *p_sys = p_filter->p_sys;
1794 ft_style_t **pp_char_styles;
1795 int *p_new_positions = NULL;
1796 int8_t *p_levels = NULL;
1797 uint8_t *pi_karaoke_bar = NULL;
1801 /* Assign each character in the text string its style explicitly, so that
1802 * after the characters have been shuffled around by Fribidi, we can re-apply
1803 * the styles, and to simplify the calculation of runs within a line.
1805 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1806 if( !pp_char_styles )
1811 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1812 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1813 * we just won't be able to display the progress bar; at least we'll
1819 for( j = 0; j < i_runs; j++ )
1820 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1821 pp_char_styles[ i++ ] = pp_styles[ j ];
1823 #if defined(HAVE_FRIBIDI)
1825 ft_style_t **pp_char_styles_new;
1826 int *p_old_positions;
1827 uint32_t *p_fribidi_string;
1828 int start_pos, pos = 0;
1830 pp_char_styles_new = (ft_style_t **)
1831 malloc( i_len * sizeof( ft_style_t * ));
1833 p_fribidi_string = (uint32_t *)
1834 malloc( (i_len + 1) * sizeof(uint32_t) );
1835 p_old_positions = (int *)
1836 malloc( (i_len + 1) * sizeof( int ) );
1837 p_new_positions = (int *)
1838 malloc( (i_len + 1) * sizeof( int ) );
1839 p_levels = (int8_t *)
1840 malloc( (i_len + 1) * sizeof( int8_t ) );
1842 if( ! pp_char_styles_new ||
1843 ! p_fribidi_string ||
1844 ! p_old_positions ||
1845 ! p_new_positions ||
1849 free( p_old_positions );
1850 free( p_new_positions );
1851 free( p_fribidi_string );
1852 free( pp_char_styles_new );
1853 free( pi_karaoke_bar );
1855 free( pp_char_styles );
1859 /* Do bidi conversion line-by-line */
1862 while(pos < i_len) {
1863 if (psz_text[pos] != '\n')
1865 p_fribidi_string[pos] = psz_text[pos];
1866 pp_char_styles_new[pos] = pp_char_styles[pos];
1867 p_new_positions[pos] = pos;
1872 while(pos < i_len) {
1873 if (psz_text[pos] == '\n')
1877 if (pos > start_pos)
1879 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1880 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1881 pos - start_pos, &base_dir,
1882 (FriBidiChar*)p_fribidi_string + start_pos,
1883 p_new_positions + start_pos,
1885 p_levels + start_pos );
1886 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1888 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1889 p_old_positions[ j - start_pos ] ];
1890 p_new_positions[ j ] += start_pos;
1894 free( p_old_positions );
1895 free( pp_char_styles );
1896 pp_char_styles = pp_char_styles_new;
1897 psz_text = p_fribidi_string;
1898 p_fribidi_string[ i_len ] = 0;
1901 /* Work out the karaoke */
1902 if( pi_karaoke_bar )
1904 int64_t i_last_duration = 0;
1905 int64_t i_duration = 0;
1906 int64_t i_start_pos = 0;
1907 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1909 for( k = 0; k< i_k_runs; k++ )
1911 double fraction = 0.0;
1913 i_duration += pi_k_durations[ k ];
1915 if( i_duration < i_elapsed )
1917 /* Completely finished this run-length -
1918 * let it render normally */
1922 else if( i_elapsed < i_last_duration )
1924 /* Haven't got up to this segment yet -
1925 * render it completely in karaoke BG mode */
1931 /* Partway through this run */
1933 fraction = (double)(i_elapsed - i_last_duration) /
1934 (double)pi_k_durations[ k ];
1936 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1938 double shade = pi_k_run_lengths[ k ] * fraction;
1940 if( p_new_positions )
1941 j = p_new_positions[ i_start_pos + i ];
1943 j = i_start_pos + i;
1945 if( i < (uint32_t)shade )
1946 pi_karaoke_bar[ j ] = 0xff;
1947 else if( (double)i > shade )
1948 pi_karaoke_bar[ j ] = 0x00;
1951 shade -= (int)shade;
1952 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1953 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1957 i_last_duration = i_duration;
1958 i_start_pos += pi_k_run_lengths[ k ];
1962 free( p_new_positions );
1964 FT_Vector tmp_result;
1966 line_desc_t *p_line = NULL;
1967 line_desc_t *p_prev = NULL;
1973 p_result->x = p_result->y = 0;
1974 tmp_result.x = tmp_result.y = 0;
1977 for( k = 0; k <= (uint32_t) i_len; k++ )
1979 if( ( k == (uint32_t) i_len ) ||
1981 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1983 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1985 /* End of the current style run */
1986 FT_Face p_face = NULL;
1989 /* Look for a match amongst our attachments first */
1990 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1994 char *psz_fontfile = NULL;
1996 psz_fontfile = FontConfig_Select( NULL,
1997 p_style->psz_fontname,
2001 if( psz_fontfile && ! *psz_fontfile )
2003 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2004 " so using default font", p_style->psz_fontname,
2005 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2006 (p_style->b_bold ? "(Bold)" :
2007 (p_style->b_italic ? "(Italic)" : ""))) );
2008 free( psz_fontfile );
2009 psz_fontfile = NULL;
2014 if( FT_New_Face( p_sys->p_library,
2015 psz_fontfile, i_idx, &p_face ) )
2017 free( psz_fontfile );
2018 free( pp_char_styles );
2019 #if defined(HAVE_FRIBIDI)
2022 free( pi_karaoke_bar );
2023 return VLC_EGENERIC;
2025 free( psz_fontfile );
2029 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2031 /* We've loaded a font face which is unhelpful for actually
2032 * rendering text - fallback to the default one.
2034 FT_Done_Face( p_face );
2038 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2039 ft_encoding_unicode ) ||
2040 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2041 p_style->i_font_size ) )
2043 if( p_face ) FT_Done_Face( p_face );
2044 free( pp_char_styles );
2045 #if defined(HAVE_FRIBIDI)
2048 free( pi_karaoke_bar );
2049 return VLC_EGENERIC;
2051 p_sys->i_use_kerning =
2052 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2055 uint32_t *psz_unicode = (uint32_t *)
2056 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2059 if( p_face ) FT_Done_Face( p_face );
2060 free( pp_char_styles );
2061 free( psz_unicode );
2062 #if defined(HAVE_FRIBIDI)
2065 free( pi_karaoke_bar );
2068 memcpy( psz_unicode, psz_text + i_prev,
2069 sizeof( uint32_t ) * ( k - i_prev ) );
2070 psz_unicode[ k - i_prev ] = 0;
2071 while( *psz_unicode )
2075 if( !(p_line = NewLine( i_len - i_prev)) )
2077 if( p_face ) FT_Done_Face( p_face );
2078 free( pp_char_styles );
2079 free( psz_unicode );
2080 #if defined(HAVE_FRIBIDI)
2083 free( pi_karaoke_bar );
2086 /* New Color mode only works in YUVA rendering mode --
2087 * (RGB mode has palette constraints on it). We therefore
2088 * need to populate the legacy colour fields also.
2090 p_line->b_new_color_mode = true;
2091 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2092 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2093 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2094 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2095 p_line->p_next = NULL;
2097 i_pen_y += tmp_result.y;
2101 if( p_prev ) p_prev->p_next = p_line;
2102 else *pp_lines = p_line;
2105 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2106 p_style->i_font_color, p_style->b_underline,
2110 p_style->i_karaoke_bg_color,
2111 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2112 &tmp_result ) != VLC_SUCCESS )
2114 if( p_face ) FT_Done_Face( p_face );
2115 free( pp_char_styles );
2116 free( psz_unicode );
2117 #if defined(HAVE_FRIBIDI)
2120 free( pi_karaoke_bar );
2121 return VLC_EGENERIC;
2126 p_result->x = __MAX( p_result->x, tmp_result.x );
2127 p_result->y += tmp_result.y;
2132 if( *psz_unicode == '\n')
2136 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2138 *c_ptr = *(c_ptr+1);
2143 free( psz_unicode );
2144 if( p_face ) FT_Done_Face( p_face );
2148 free( pp_char_styles );
2149 #if defined(HAVE_FRIBIDI)
2154 p_result->x = __MAX( p_result->x, tmp_result.x );
2155 p_result->y += tmp_result.y;
2158 if( pi_karaoke_bar )
2161 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2163 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2165 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2169 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2171 /* 100% BG colour will render faster if we
2172 * instead make it 100% FG colour, so leave
2173 * the ratio alone and copy the value across
2175 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2179 if( pi_karaoke_bar[ i ] & 0x80 )
2181 /* Swap Left and Right sides over for Right aligned
2182 * language text (eg. Arabic, Hebrew)
2184 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2186 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2187 p_line->p_bg_rgb[ k ] = i_tmp;
2189 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2192 /* Jump over the '\n' at the line-end */
2195 free( pi_karaoke_bar );
2201 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2202 subpicture_region_t *p_region_in )
2204 int rv = VLC_SUCCESS;
2205 stream_t *p_sub = NULL;
2206 xml_reader_t *p_xml_reader = NULL;
2208 if( !p_region_in || !p_region_in->psz_html )
2209 return VLC_EGENERIC;
2211 /* Reset the default fontsize in case screen metrics have changed */
2212 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2214 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2215 (uint8_t *) p_region_in->psz_html,
2216 strlen( p_region_in->psz_html ),
2220 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2221 if( p_filter->p_sys->p_xml )
2223 bool b_karaoke = false;
2225 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2228 /* Look for Root Node */
2229 if( xml_ReaderRead( p_xml_reader ) == 1 )
2231 char *psz_node = xml_ReaderName( p_xml_reader );
2233 if( !strcasecmp( "karaoke", psz_node ) )
2235 /* We're going to have to render the text a number
2236 * of times to show the progress marker on the text.
2238 var_SetBool( p_filter, "text-rerender", true );
2241 else if( !strcasecmp( "text", psz_node ) )
2247 /* Only text and karaoke tags are supported */
2248 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2249 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2250 p_xml_reader = NULL;
2262 uint32_t i_runs = 0;
2263 uint32_t i_k_runs = 0;
2264 uint32_t *pi_run_lengths = NULL;
2265 uint32_t *pi_k_run_lengths = NULL;
2266 uint32_t *pi_k_durations = NULL;
2267 ft_style_t **pp_styles = NULL;
2269 line_desc_t *p_lines = NULL;
2271 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2272 sizeof( uint32_t ) );
2277 rv = ProcessNodes( p_filter, p_xml_reader,
2278 p_region_in->p_style, psz_text, &i_len,
2279 &i_runs, &pi_run_lengths, &pp_styles,
2281 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2284 p_region_out->i_x = p_region_in->i_x;
2285 p_region_out->i_y = p_region_in->i_y;
2287 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2289 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2290 pi_run_lengths, pp_styles, &p_lines, &result,
2291 b_karaoke, i_k_runs, pi_k_run_lengths,
2295 for( k=0; k<i_runs; k++)
2296 DeleteStyle( pp_styles[k] );
2298 free( pi_run_lengths );
2301 /* Don't attempt to render text that couldn't be layed out
2304 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2306 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2308 Render( p_filter, p_region_out, p_lines,
2309 result.x, result.y );
2313 RenderYUVA( p_filter, p_region_out, p_lines,
2314 result.x, result.y );
2318 FreeLines( p_lines );
2320 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2323 stream_Delete( p_sub );
2329 static char* FontConfig_Select( FcConfig* priv, const char* family,
2330 bool b_bold, bool b_italic, int *i_idx )
2333 FcPattern *pat, *p_pat;
2337 pat = FcPatternCreate();
2338 if (!pat) return NULL;
2340 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2341 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2342 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2343 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2345 FcDefaultSubstitute( pat );
2347 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2349 FcPatternDestroy( pat );
2353 p_pat = FcFontMatch( priv, pat, &result );
2354 FcPatternDestroy( pat );
2355 if( !p_pat ) return NULL;
2357 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2358 || ( val_b != FcTrue ) )
2360 FcPatternDestroy( p_pat );
2363 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2368 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2370 FcPatternDestroy( p_pat );
2375 if( strcasecmp((const char*)val_s, family ) != 0 )
2376 msg_Warn( p_filter, "fontconfig: selected font family is not"
2377 "the requested one: '%s' != '%s'\n",
2378 (const char*)val_s, family );
2381 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2383 FcPatternDestroy( p_pat );
2387 FcPatternDestroy( p_pat );
2388 return strdup( (const char*)val_s );
2392 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2393 uint32_t **psz_text_out, uint32_t *pi_runs,
2394 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2395 ft_style_t *p_style )
2397 VLC_UNUSED(p_filter);
2398 VLC_UNUSED(psz_text_in);
2399 VLC_UNUSED(psz_text_out);
2400 VLC_UNUSED(pi_runs);
2401 VLC_UNUSED(ppi_run_lengths);
2402 VLC_UNUSED(ppp_styles);
2403 VLC_UNUSED(p_style);
2406 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2407 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2408 bool b_uline, bool b_through )
2411 VLC_UNUSED(p_fonts);
2413 VLC_UNUSED(b_italic);
2414 VLC_UNUSED(b_uline);
2415 VLC_UNUSED(b_through);
2420 static void FreeLine( line_desc_t *p_line )
2423 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2425 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2427 free( p_line->pp_glyphs );
2428 free( p_line->p_glyph_pos );
2429 free( p_line->p_fg_rgb );
2430 free( p_line->p_bg_rgb );
2431 free( p_line->p_fg_bg_ratio );
2432 free( p_line->pi_underline_offset );
2433 free( p_line->pi_underline_thickness );
2437 static void FreeLines( line_desc_t *p_lines )
2439 line_desc_t *p_line, *p_next;
2441 if( !p_lines ) return;
2443 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2445 p_next = p_line->p_next;
2450 static line_desc_t *NewLine( int i_count )
2452 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2454 if( !p_line ) return NULL;
2455 p_line->i_height = 0;
2456 p_line->i_width = 0;
2457 p_line->p_next = NULL;
2459 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2460 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2461 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2462 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2463 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2464 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2465 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2466 if( ( p_line->pp_glyphs == NULL ) ||
2467 ( p_line->p_glyph_pos == NULL ) ||
2468 ( p_line->p_fg_rgb == NULL ) ||
2469 ( p_line->p_bg_rgb == NULL ) ||
2470 ( p_line->p_fg_bg_ratio == NULL ) ||
2471 ( p_line->pi_underline_offset == NULL ) ||
2472 ( p_line->pi_underline_thickness == NULL ) )
2474 free( p_line->pi_underline_thickness );
2475 free( p_line->pi_underline_offset );
2476 free( p_line->p_fg_rgb );
2477 free( p_line->p_bg_rgb );
2478 free( p_line->p_fg_bg_ratio );
2479 free( p_line->p_glyph_pos );
2480 free( p_line->pp_glyphs );
2484 p_line->pp_glyphs[0] = NULL;
2485 p_line->b_new_color_mode = false;
2490 static int GetFontSize( filter_t *p_filter )
2492 filter_sys_t *p_sys = p_filter->p_sys;
2496 if( p_sys->i_default_font_size )
2498 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2499 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2501 i_size = p_sys->i_default_font_size;
2505 var_Get( p_filter, "freetype-rel-fontsize", &val );
2508 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2509 p_filter->p_sys->i_display_height =
2510 p_filter->fmt_out.video.i_height;
2515 msg_Warn( p_filter, "invalid fontsize, using 12" );
2516 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2517 i_size = 12 * val.i_int / 1000;
2524 static int SetFontSize( filter_t *p_filter, int i_size )
2526 filter_sys_t *p_sys = p_filter->p_sys;
2530 i_size = GetFontSize( p_filter );
2532 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2535 p_sys->i_font_size = i_size;
2537 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2539 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2540 return VLC_EGENERIC;
2546 static void YUVFromRGB( uint32_t i_argb,
2547 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2549 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2550 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2551 int i_blue = ( i_argb & 0x000000ff );
2553 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2554 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2555 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2556 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2557 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2558 -585 * i_blue + 4096 + 1048576) >> 13, 240);