1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include <vlc_stream.h>
39 #include <vlc_input.h>
40 #include <vlc_strings.h>
41 #include <vlc_dialog.h>
42 #include <vlc_memory.h>
47 #include <freetype/ftsynth.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
57 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
58 #define FC_DEFAULT_FONT "Arial Black"
59 #elif defined( SYS_BEOS )
60 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
61 #define FC_DEFAULT_FONT "Swiss"
62 #elif defined( WIN32 )
63 #define DEFAULT_FONT "" /* Default font found at run-time */
64 #define FC_DEFAULT_FONT "Arial"
65 #elif defined( HAVE_MAEMO )
66 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
67 #define FC_DEFAULT_FONT "Nokia Sans Bold"
69 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
70 #define FC_DEFAULT_FONT "Serif Bold"
73 #if defined(HAVE_FRIBIDI)
74 #include <fribidi/fribidi.h>
77 #ifdef HAVE_FONTCONFIG
78 #include <fontconfig/fontconfig.h>
80 #define DEFAULT_FONT FC_DEFAULT_FONT
85 /*****************************************************************************
87 *****************************************************************************/
88 static int Create ( vlc_object_t * );
89 static void Destroy( vlc_object_t * );
91 #define FONT_TEXT N_("Font")
93 #ifdef HAVE_FONTCONFIG
94 #define FONT_LONGTEXT N_("Font family for the font you want to use")
96 #define FONT_LONGTEXT N_("Font file for the font you want to use")
99 #define FONTSIZE_TEXT N_("Font size in pixels")
100 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
101 "that will be rendered on the video. " \
102 "If set to something different than 0 this option will override the " \
103 "relative font size." )
104 #define OPACITY_TEXT N_("Opacity")
105 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
106 "text that will be rendered on the video. 0 = transparent, " \
107 "255 = totally opaque. " )
108 #define COLOR_TEXT N_("Text default color")
109 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
110 "the video. This must be an hexadecimal (like HTML colors). The first two "\
111 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
112 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
113 #define FONTSIZER_TEXT N_("Relative font size")
114 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
115 "fonts that will be rendered on the video. If absolute font size is set, "\
116 "relative size will be overridden." )
118 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
119 static const char *const ppsz_sizes_text[] = {
120 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
121 #define YUVP_TEXT N_("Use YUVP renderer")
122 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
123 "This option is only needed if you want to encode into DVB subtitles" )
124 #define EFFECT_TEXT N_("Font Effect")
125 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
126 "text to improve its readability." )
128 #define EFFECT_BACKGROUND 1
129 #define EFFECT_OUTLINE 2
130 #define EFFECT_OUTLINE_FAT 3
132 static int const pi_effects[] = { 1, 2, 3 };
133 static const char *const ppsz_effects_text[] = {
134 N_("Background"),N_("Outline"), N_("Fat Outline") };
135 static const int pi_color_values[] = {
136 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
137 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
138 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
140 static const char *const ppsz_color_descriptions[] = {
141 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
142 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
143 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
146 set_shortname( N_("Text renderer"))
147 set_description( N_("Freetype2 font renderer") )
148 set_category( CAT_VIDEO )
149 set_subcategory( SUBCAT_VIDEO_SUBPIC )
151 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
154 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
155 FONTSIZE_LONGTEXT, true )
157 /* opacity valid on 0..255, with default 255 = fully opaque */
158 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
159 OPACITY_TEXT, OPACITY_LONGTEXT, true )
161 /* hook to the color values list, with default 0x00ffffff = white */
162 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
163 COLOR_LONGTEXT, false )
164 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
166 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
167 FONTSIZER_LONGTEXT, false )
168 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
169 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
170 EFFECT_LONGTEXT, false )
171 change_integer_list( pi_effects, ppsz_effects_text, NULL )
173 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
174 YUVP_LONGTEXT, true )
175 set_capability( "text renderer", 100 )
176 add_shortcut( "text" )
177 set_callbacks( Create, Destroy )
182 /*****************************************************************************
184 *****************************************************************************/
186 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
187 static int RenderText( filter_t *, subpicture_region_t *,
188 subpicture_region_t * );
189 #ifdef HAVE_FONTCONFIG
190 static int RenderHtml( filter_t *, subpicture_region_t *,
191 subpicture_region_t * );
192 static char *FontConfig_Select( FcConfig *, const char *,
197 static int LoadFontsFromAttachments( filter_t *p_filter );
199 static int GetFontSize( filter_t *p_filter );
200 static int SetFontSize( filter_t *, int );
201 static void YUVFromRGB( uint32_t i_argb,
202 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
204 typedef struct line_desc_t line_desc_t;
207 /** NULL-terminated list of glyphs making the string */
208 FT_BitmapGlyph *pp_glyphs;
209 /** list of relative positions for the glyphs */
210 FT_Vector *p_glyph_pos;
211 /** list of RGB information for styled text
212 * -- if the rendering mode supports it (RenderYUVA) and
213 * b_new_color_mode is set, then it becomes possible to
214 * have multicoloured text within the subtitles. */
217 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
218 bool b_new_color_mode;
219 /** underline information -- only supplied if text should be underlined */
220 int *pi_underline_offset;
221 uint16_t *pi_underline_thickness;
225 int i_red, i_green, i_blue;
230 static line_desc_t *NewLine( int );
235 uint32_t i_font_color; /* ARGB */
236 uint32_t i_karaoke_bg_color; /* ARGB */
244 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
245 static void FreeLines( line_desc_t * );
246 static void FreeLine( line_desc_t * );
248 /*****************************************************************************
249 * filter_sys_t: freetype local data
250 *****************************************************************************
251 * This structure is part of the video output thread descriptor.
252 * It describes the freetype specific properties of an output thread.
253 *****************************************************************************/
256 FT_Library p_library; /* handle to library */
257 FT_Face p_face; /* handle to face object */
259 uint8_t i_font_opacity;
264 int i_default_font_size;
265 int i_display_height;
266 #ifdef HAVE_FONTCONFIG
267 char* psz_fontfamily;
271 input_attachment_t **pp_font_attachments;
272 int i_font_attachments;
276 #define UCHAR uint32_t
277 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
278 #define TR_FONT_STYLE_PTR ft_style_t *
280 #include "text_renderer.h"
282 /*****************************************************************************
283 * Create: allocates osd-text video thread output method
284 *****************************************************************************
285 * This function allocates and initializes a Clone vout method.
286 *****************************************************************************/
287 static int Create( vlc_object_t *p_this )
289 filter_t *p_filter = (filter_t *)p_this;
291 char *psz_fontfile=NULL;
292 char *psz_fontfamily=NULL;
293 int i_error,fontindex;
295 #ifdef HAVE_FONTCONFIG
296 FcPattern *fontpattern = NULL, *fontmatch = NULL;
297 /* Initialise result to Match, as fontconfig doesnt
298 * really set this other than some error-cases */
299 FcResult fontresult = FcResultMatch;
303 /* Allocate structure */
304 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
307 #ifdef HAVE_FONTCONFIG
308 p_sys->psz_fontfamily = NULL;
312 p_sys->p_library = 0;
313 p_sys->i_font_size = 0;
314 p_sys->i_display_height = 0;
316 var_Create( p_filter, "freetype-rel-fontsize",
317 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
319 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
320 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
321 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
322 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
323 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
324 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
325 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
328 if( !psz_fontfamily || !*psz_fontfamily )
330 free( psz_fontfamily );
331 #ifdef HAVE_FONTCONFIG
332 psz_fontfamily=strdup( DEFAULT_FONT );
334 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
335 if( !psz_fontfamily )
338 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
339 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
341 strcpy( psz_fontfamily, DEFAULT_FONT );
343 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
347 #ifdef HAVE_FONTCONFIG
348 msg_Dbg( p_filter, "Building font databases.");
353 dialog_progress_bar_t *p_dialog = NULL;
354 FcConfig *fcConfig = FcInitLoadConfig();
356 p_dialog = dialog_ProgressCreate( p_filter,
357 _("Building font cache"),
358 _("Please wait while your font cache is rebuilt.\n"
359 "This should take less than a few minutes."), NULL );
362 dialog_ProgressSet( p_dialog, NULL, 0.5 );
364 FcConfigBuildFonts( fcConfig );
366 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
370 dialog_ProgressSet( p_dialog, NULL, 1.0 );
371 dialog_ProgressDestroy( p_dialog );
375 /* Lets find some fontfile from freetype-font variable family */
377 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
380 fontpattern = FcPatternCreate();
383 msg_Err( p_filter, "Creating fontpattern failed");
387 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
388 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
389 free( psz_fontsize );
391 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
393 msg_Err( p_filter, "FontSubstitute failed");
396 FcDefaultSubstitute( fontpattern );
398 /* testing fontresult here doesn't do any good really, but maybe it will
399 * in future as fontconfig code doesn't set it in all cases and just
400 * returns NULL or doesn't set to to Match on all Match cases.*/
401 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
402 if( !fontmatch || fontresult == FcResultNoMatch )
404 msg_Err( p_filter, "Fontmatching failed");
408 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
409 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
412 msg_Err( p_filter, "Failed to get fontfile");
416 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
417 psz_fontfile ? psz_fontfile : "(null)" );
418 p_sys->psz_fontfamily = strdup( psz_fontfamily );
422 psz_fontfile = psz_fontfamily;
426 i_error = FT_Init_FreeType( &p_sys->p_library );
429 msg_Err( p_filter, "couldn't initialize freetype" );
433 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
434 fontindex, &p_sys->p_face );
436 if( i_error == FT_Err_Unknown_File_Format )
438 msg_Err( p_filter, "file %s have unknown format",
439 psz_fontfile ? psz_fontfile : "(null)" );
444 msg_Err( p_filter, "failed to load font file %s",
445 psz_fontfile ? psz_fontfile : "(null)" );
449 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
452 msg_Err( p_filter, "font has no unicode translation table" );
456 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
458 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
461 p_sys->pp_font_attachments = NULL;
462 p_sys->i_font_attachments = 0;
464 p_filter->pf_render_text = RenderText;
465 #ifdef HAVE_FONTCONFIG
466 p_filter->pf_render_html = RenderHtml;
467 FcPatternDestroy( fontmatch );
468 FcPatternDestroy( fontpattern );
470 p_filter->pf_render_html = NULL;
473 free( psz_fontfamily );
474 LoadFontsFromAttachments( p_filter );
479 #ifdef HAVE_FONTCONFIG
480 if( fontmatch ) FcPatternDestroy( fontmatch );
481 if( fontpattern ) FcPatternDestroy( fontpattern );
486 dialog_ProgressDestroy( p_dialog );
489 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
490 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
491 free( psz_fontfamily );
496 /*****************************************************************************
497 * Destroy: destroy Clone video thread output method
498 *****************************************************************************
499 * Clean up all data and library connections
500 *****************************************************************************/
501 static void Destroy( vlc_object_t *p_this )
503 filter_t *p_filter = (filter_t *)p_this;
504 filter_sys_t *p_sys = p_filter->p_sys;
506 if( p_sys->pp_font_attachments )
510 for( k = 0; k < p_sys->i_font_attachments; k++ )
511 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
513 free( p_sys->pp_font_attachments );
516 #ifdef HAVE_FONTCONFIG
517 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
518 free( p_sys->psz_fontfamily );
521 /* FcFini asserts calling the subfunction FcCacheFini()
522 * even if no other library functions have been made since FcInit(),
523 * so don't call it. */
525 FT_Done_Face( p_sys->p_face );
526 FT_Done_FreeType( p_sys->p_library );
530 /*****************************************************************************
531 * Make any TTF/OTF fonts present in the attachments of the media file
532 * and store them for later use by the FreeType Engine
533 *****************************************************************************/
534 static int LoadFontsFromAttachments( filter_t *p_filter )
536 filter_sys_t *p_sys = p_filter->p_sys;
537 input_thread_t *p_input;
538 input_attachment_t **pp_attachments;
539 int i_attachments_cnt;
541 int rv = VLC_SUCCESS;
543 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
547 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
549 vlc_object_release(p_input);
553 p_sys->i_font_attachments = 0;
554 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
555 if(! p_sys->pp_font_attachments )
558 for( k = 0; k < i_attachments_cnt; k++ )
560 input_attachment_t *p_attach = pp_attachments[k];
562 if( p_sys->pp_font_attachments )
564 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
565 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
566 ( p_attach->i_data > 0 ) &&
567 ( p_attach->p_data != NULL ) )
569 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
573 vlc_input_attachment_Delete( p_attach );
578 vlc_input_attachment_Delete( p_attach );
581 free( pp_attachments );
583 vlc_object_release(p_input);
588 /*****************************************************************************
589 * Render: place string in picture
590 *****************************************************************************
591 * This function merges the previously rendered freetype glyphs into a picture
592 *****************************************************************************/
593 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
594 line_desc_t *p_line, int i_width, int i_height )
596 VLC_UNUSED(p_filter);
597 static const uint8_t pi_gamma[16] =
598 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
599 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
603 int i, x, y, i_pitch;
604 uint8_t i_y; /* YUV values, derived from incoming RGB */
607 /* Create a new subpicture region */
608 memset( &fmt, 0, sizeof(video_format_t) );
609 fmt.i_chroma = VLC_CODEC_YUVP;
610 fmt.i_width = fmt.i_visible_width = i_width + 4;
611 fmt.i_height = fmt.i_visible_height = i_height + 4;
612 if( p_region->fmt.i_visible_width > 0 )
613 fmt.i_visible_width = p_region->fmt.i_visible_width;
614 if( p_region->fmt.i_visible_height > 0 )
615 fmt.i_visible_height = p_region->fmt.i_visible_height;
616 fmt.i_x_offset = fmt.i_y_offset = 0;
618 assert( !p_region->p_picture );
619 p_region->p_picture = picture_NewFromFormat( &fmt );
620 if( !p_region->p_picture )
622 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
625 /* Calculate text color components */
626 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
627 25 * p_line->i_blue + 128) >> 8) + 16;
628 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
629 112 * p_line->i_blue + 128) >> 8) + 128;
630 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
631 18 * p_line->i_blue + 128) >> 8) + 128;
634 fmt.p_palette->i_entries = 16;
635 for( i = 0; i < 8; i++ )
637 fmt.p_palette->palette[i][0] = 0;
638 fmt.p_palette->palette[i][1] = 0x80;
639 fmt.p_palette->palette[i][2] = 0x80;
640 fmt.p_palette->palette[i][3] = pi_gamma[i];
641 fmt.p_palette->palette[i][3] =
642 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
644 for( i = 8; i < fmt.p_palette->i_entries; i++ )
646 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
647 fmt.p_palette->palette[i][1] = i_u;
648 fmt.p_palette->palette[i][2] = i_v;
649 fmt.p_palette->palette[i][3] = pi_gamma[i];
650 fmt.p_palette->palette[i][3] =
651 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
654 p_dst = p_region->p_picture->Y_PIXELS;
655 i_pitch = p_region->p_picture->Y_PITCH;
657 /* Initialize the region pixels */
658 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
660 for( ; p_line != NULL; p_line = p_line->p_next )
662 int i_glyph_tmax = 0;
663 int i_bitmap_offset, i_offset, i_align_offset = 0;
664 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
666 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
667 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
670 if( p_line->i_width < i_width )
672 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
674 i_align_offset = i_width - p_line->i_width;
676 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
678 i_align_offset = ( i_width - p_line->i_width ) / 2;
682 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
684 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
686 i_offset = ( p_line->p_glyph_pos[ i ].y +
687 i_glyph_tmax - p_glyph->top + 2 ) *
688 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
691 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
693 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
695 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
697 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
704 /* Outlining (find something better than nearest neighbour filtering ?) */
707 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
708 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
709 uint8_t left, current;
711 for( y = 1; y < (int)fmt.i_height - 1; y++ )
713 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
714 p_dst += p_region->p_picture->Y_PITCH;
717 for( x = 1; x < (int)fmt.i_width - 1; x++ )
720 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
721 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;
725 memset( p_top, 0, fmt.i_width );
731 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
732 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
733 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
734 int i_glyph_tmax, int i_align_offset,
735 uint8_t i_y, uint8_t i_u, uint8_t i_v,
736 subpicture_region_t *p_region)
740 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
742 p_dst_y = p_region->p_picture->Y_PIXELS;
743 p_dst_u = p_region->p_picture->U_PIXELS;
744 p_dst_v = p_region->p_picture->V_PIXELS;
745 p_dst_a = p_region->p_picture->A_PIXELS;
746 i_pitch = p_region->p_picture->A_PITCH;
748 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
749 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
751 for( y = 0; y < i_line_thickness; y++ )
753 int i_extra = p_this_glyph->bitmap.width;
757 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
758 (p_this_glyph_pos->x + p_this_glyph->left);
760 for( x = 0; x < i_extra; x++ )
764 /* break the underline around the tails of any glyphs which cross it */
765 /* Strikethrough doesn't get broken */
766 for( z = x - i_line_thickness;
767 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
770 if( p_next_glyph && ( z >= i_extra ) )
772 int i_row = i_line_offset + p_next_glyph->top + y;
774 if( ( p_next_glyph->bitmap.rows > i_row ) &&
775 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
780 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
782 int i_row = i_line_offset + p_this_glyph->top + y;
784 if( ( p_this_glyph->bitmap.rows > i_row ) &&
785 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
794 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
795 p_dst_u[i_offset+x] = i_u;
796 p_dst_v[i_offset+x] = i_v;
797 p_dst_a[i_offset+x] = 255;
804 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
806 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
807 int i_pitch = p_region->p_picture->A_PITCH;
810 for( ; p_line != NULL; p_line = p_line->p_next )
812 int i_glyph_tmax=0, i = 0;
813 int i_bitmap_offset, i_offset, i_align_offset = 0;
814 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
816 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
817 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
820 if( p_line->i_width < i_width )
822 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
824 i_align_offset = i_width - p_line->i_width;
826 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
828 i_align_offset = ( i_width - p_line->i_width ) / 2;
832 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
834 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
836 i_offset = ( p_line->p_glyph_pos[ i ].y +
837 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
838 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
839 i_align_offset +xoffset;
841 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
843 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
845 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
846 if( p_dst[i_offset+x] <
847 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
849 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
858 /*****************************************************************************
859 * Render: place string in picture
860 *****************************************************************************
861 * This function merges the previously rendered freetype glyphs into a picture
862 *****************************************************************************/
863 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
864 line_desc_t *p_line, int i_width, int i_height )
866 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
868 int i, x, y, i_pitch, i_alpha;
869 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
871 if( i_width == 0 || i_height == 0 )
874 /* Create a new subpicture region */
875 memset( &fmt, 0, sizeof(video_format_t) );
876 fmt.i_chroma = VLC_CODEC_YUVA;
877 fmt.i_width = fmt.i_visible_width = i_width + 6;
878 fmt.i_height = fmt.i_visible_height = i_height + 6;
879 if( p_region->fmt.i_visible_width > 0 )
880 fmt.i_visible_width = p_region->fmt.i_visible_width;
881 if( p_region->fmt.i_visible_height > 0 )
882 fmt.i_visible_height = p_region->fmt.i_visible_height;
883 fmt.i_x_offset = fmt.i_y_offset = 0;
885 p_region->p_picture = picture_NewFromFormat( &fmt );
886 if( !p_region->p_picture )
890 /* Calculate text color components */
891 YUVFromRGB( (p_line->i_red << 16) |
892 (p_line->i_green << 8) |
895 i_alpha = p_line->i_alpha;
897 p_dst_y = p_region->p_picture->Y_PIXELS;
898 p_dst_u = p_region->p_picture->U_PIXELS;
899 p_dst_v = p_region->p_picture->V_PIXELS;
900 p_dst_a = p_region->p_picture->A_PIXELS;
901 i_pitch = p_region->p_picture->A_PITCH;
903 /* Initialize the region pixels */
904 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
906 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
907 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
908 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
909 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
913 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
914 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
915 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
916 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
918 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
919 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
921 DrawBlack( p_line, i_width, p_region, 0, 0);
922 DrawBlack( p_line, i_width, p_region, -1, 0);
923 DrawBlack( p_line, i_width, p_region, 0, -1);
924 DrawBlack( p_line, i_width, p_region, 1, 0);
925 DrawBlack( p_line, i_width, p_region, 0, 1);
928 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
930 DrawBlack( p_line, i_width, p_region, -1, -1);
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);
935 DrawBlack( p_line, i_width, p_region, -2, 0);
936 DrawBlack( p_line, i_width, p_region, 0, -2);
937 DrawBlack( p_line, i_width, p_region, 2, 0);
938 DrawBlack( p_line, i_width, p_region, 0, 2);
940 DrawBlack( p_line, i_width, p_region, -2, -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);
945 DrawBlack( p_line, i_width, p_region, -3, 0);
946 DrawBlack( p_line, i_width, p_region, 0, -3);
947 DrawBlack( p_line, i_width, p_region, 3, 0);
948 DrawBlack( p_line, i_width, p_region, 0, 3);
951 for( ; p_line != NULL; p_line = p_line->p_next )
953 int i_glyph_tmax = 0;
954 int i_bitmap_offset, i_offset, i_align_offset = 0;
955 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
957 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
958 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
961 if( p_line->i_width < i_width )
963 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
965 i_align_offset = i_width - p_line->i_width;
967 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
969 i_align_offset = ( i_width - p_line->i_width ) / 2;
973 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
975 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
977 i_offset = ( p_line->p_glyph_pos[ i ].y +
978 i_glyph_tmax - p_glyph->top + 3 ) *
979 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
982 if( p_line->b_new_color_mode )
984 /* Every glyph can (and in fact must) have its own color */
985 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
988 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
990 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
992 uint8_t i_y_local = i_y;
993 uint8_t i_u_local = i_u;
994 uint8_t i_v_local = i_v;
996 if( p_line->p_fg_bg_ratio != 0x00 )
998 int i_split = p_glyph->bitmap.width *
999 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1003 YUVFromRGB( p_line->p_bg_rgb[ i ],
1004 &i_y_local, &i_u_local, &i_v_local );
1008 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1010 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1011 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1013 p_dst_u[i_offset+x] = i_u;
1014 p_dst_v[i_offset+x] = i_v;
1016 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1017 p_dst_a[i_offset+x] = 0xff;
1020 i_offset += i_pitch;
1023 if( p_line->pi_underline_thickness[ i ] )
1025 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1026 p_line->pi_underline_offset[ i ],
1027 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1028 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1029 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1030 i_glyph_tmax, i_align_offset,
1037 /* Apply the alpha setting */
1038 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1039 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1045 * This function renders a text subpicture region into another one.
1046 * It also calculates the size needed for this string, and renders the
1047 * needed glyphs into memory. It is used as pf_add_string callback in
1048 * the vout method by this module
1050 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1051 subpicture_region_t *p_region_in )
1053 filter_sys_t *p_sys = p_filter->p_sys;
1054 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1055 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1056 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1057 int i_string_length;
1059 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1060 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1070 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1071 psz_string = p_region_in->psz_text;
1072 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1074 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1075 i_scale = val.i_int;
1077 if( p_region_in->p_style )
1079 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1080 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1081 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1085 i_font_color = p_sys->i_font_color;
1086 i_font_alpha = 255 - p_sys->i_font_opacity;
1087 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1090 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1091 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1092 SetFontSize( p_filter, i_font_size );
1094 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1095 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1096 i_blue = i_font_color & 0x000000FF;
1098 result.x = result.y = 0;
1099 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1101 psz_unicode = psz_unicode_orig =
1102 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1103 if( psz_unicode == NULL )
1105 #if defined(WORDS_BIGENDIAN)
1106 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1108 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1110 if( iconv_handle == (vlc_iconv_t)-1 )
1112 msg_Warn( p_filter, "unable to do conversion" );
1118 const char *p_in_buffer = psz_string;
1119 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1120 i_in_bytes = strlen( psz_string );
1121 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1122 i_out_bytes_left = i_out_bytes;
1123 p_out_buffer = (char *)psz_unicode;
1124 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1126 &p_out_buffer, &i_out_bytes_left );
1128 vlc_iconv_close( iconv_handle );
1132 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1133 "bytes left %u", (unsigned)i_in_bytes );
1136 *(uint32_t*)p_out_buffer = 0;
1137 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1140 #if defined(HAVE_FRIBIDI)
1142 uint32_t *p_fribidi_string;
1143 int32_t start_pos, pos = 0;
1145 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1146 if( !p_fribidi_string )
1149 /* Do bidi conversion line-by-line */
1150 while( pos < i_string_length )
1152 while( pos < i_string_length )
1154 i_char = psz_unicode[pos];
1155 if (i_char != '\r' && i_char != '\n')
1157 p_fribidi_string[pos] = i_char;
1161 while( pos < i_string_length )
1163 i_char = psz_unicode[pos];
1164 if (i_char == '\r' || i_char == '\n')
1168 if (pos > start_pos)
1170 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1171 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1174 (FriBidiChar*)p_fribidi_string + start_pos,
1179 free( psz_unicode_orig );
1180 psz_unicode = psz_unicode_orig = p_fribidi_string;
1181 p_fribidi_string[ i_string_length ] = 0;
1185 /* Calculate relative glyph positions and a bounding box for the
1187 if( !(p_line = NewLine( strlen( psz_string ))) )
1190 i_pen_x = i_pen_y = 0;
1192 psz_line_start = psz_unicode;
1194 #define face p_sys->p_face
1195 #define glyph face->glyph
1197 while( *psz_unicode )
1199 i_char = *psz_unicode++;
1200 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1205 if( i_char == '\n' )
1207 psz_line_start = psz_unicode;
1208 if( !(p_next = NewLine( strlen( psz_string ))) )
1210 p_line->p_next = p_next;
1211 p_line->i_width = line.xMax;
1212 p_line->i_height = face->size->metrics.height >> 6;
1213 p_line->pp_glyphs[ i ] = NULL;
1214 p_line->i_alpha = i_font_alpha;
1215 p_line->i_red = i_red;
1216 p_line->i_green = i_green;
1217 p_line->i_blue = i_blue;
1220 result.x = __MAX( result.x, line.xMax );
1221 result.y += face->size->metrics.height >> 6;
1224 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1225 i_pen_y += face->size->metrics.height >> 6;
1227 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1232 i_glyph_index = FT_Get_Char_Index( face, i_char );
1233 if( p_sys->i_use_kerning && i_glyph_index
1237 FT_Get_Kerning( face, i_previous, i_glyph_index,
1238 ft_kerning_default, &delta );
1239 i_pen_x += delta.x >> 6;
1242 p_line->p_glyph_pos[ i ].x = i_pen_x;
1243 p_line->p_glyph_pos[ i ].y = i_pen_y;
1244 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1247 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1251 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1254 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1258 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1259 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1262 FT_Done_Glyph( tmp_glyph );
1265 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1268 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1269 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1270 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1272 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1273 p_line->pp_glyphs[ i ] = NULL;
1275 p_line = NewLine( strlen( psz_string ));
1276 if( p_prev ) p_prev->p_next = p_line;
1277 else p_lines = p_line;
1279 uint32_t *psz_unicode_saved = psz_unicode;
1280 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1284 if( psz_unicode == psz_line_start )
1285 { /* try harder to break that line */
1286 psz_unicode = psz_unicode_saved;
1287 while( psz_unicode > psz_line_start &&
1288 *psz_unicode != '_' && *psz_unicode != '/' &&
1289 *psz_unicode != '\\' && *psz_unicode != '.' )
1294 if( psz_unicode == psz_line_start )
1296 msg_Warn( p_filter, "unbreakable string" );
1301 *psz_unicode = '\n';
1303 psz_unicode = psz_line_start;
1306 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1309 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1310 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1312 i_previous = i_glyph_index;
1313 i_pen_x += glyph->advance.x >> 6;
1317 p_line->i_width = line.xMax;
1318 p_line->i_height = face->size->metrics.height >> 6;
1319 p_line->pp_glyphs[ i ] = NULL;
1320 p_line->i_alpha = i_font_alpha;
1321 p_line->i_red = i_red;
1322 p_line->i_green = i_green;
1323 p_line->i_blue = i_blue;
1324 result.x = __MAX( result.x, line.xMax );
1325 result.y += line.yMax - line.yMin;
1330 p_region_out->i_x = p_region_in->i_x;
1331 p_region_out->i_y = p_region_in->i_y;
1333 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1334 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1336 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1338 free( psz_unicode_orig );
1339 FreeLines( p_lines );
1343 free( psz_unicode_orig );
1344 FreeLines( p_lines );
1345 return VLC_EGENERIC;
1348 #ifdef HAVE_FONTCONFIG
1349 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1350 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1351 bool b_italic, bool b_uline, bool b_through )
1353 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1357 p_style->i_font_size = i_font_size;
1358 p_style->i_font_color = i_font_color;
1359 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1360 p_style->b_italic = b_italic;
1361 p_style->b_bold = b_bold;
1362 p_style->b_underline = b_uline;
1363 p_style->b_through = b_through;
1365 p_style->psz_fontname = strdup( psz_fontname );
1370 static void DeleteStyle( ft_style_t *p_style )
1374 free( p_style->psz_fontname );
1379 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1386 if(( s1->i_font_size == s2->i_font_size ) &&
1387 ( s1->i_font_color == s2->i_font_color ) &&
1388 ( s1->b_italic == s2->b_italic ) &&
1389 ( s1->b_through == s2->b_through ) &&
1390 ( s1->b_bold == s2->b_bold ) &&
1391 ( s1->b_underline == s2->b_underline ) &&
1392 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1399 static void IconvText( filter_t *p_filter, const char *psz_string,
1400 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1402 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1404 /* If memory hasn't been allocated for our output string, allocate it here
1405 * - the calling function must now be responsible for freeing it.
1407 if( !*ppsz_unicode )
1408 *ppsz_unicode = (uint32_t *)
1409 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1411 /* We don't need to handle a NULL pointer in *ppsz_unicode
1412 * if we are instead testing for a non NULL value like we are here */
1416 #if defined(WORDS_BIGENDIAN)
1417 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1419 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1421 if( iconv_handle != (vlc_iconv_t)-1 )
1423 char *p_in_buffer, *p_out_buffer;
1424 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1425 i_in_bytes = strlen( psz_string );
1426 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1427 i_out_bytes_left = i_out_bytes;
1428 p_in_buffer = (char *) psz_string;
1429 p_out_buffer = (char *) *ppsz_unicode;
1430 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1431 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1433 vlc_iconv_close( iconv_handle );
1437 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1438 "bytes left %u", (unsigned)i_in_bytes );
1442 *(uint32_t*)p_out_buffer = 0;
1444 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1449 msg_Warn( p_filter, "unable to do conversion" );
1454 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1455 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1456 bool b_uline, bool b_through )
1458 ft_style_t *p_style = NULL;
1460 char *psz_fontname = NULL;
1461 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1462 uint32_t i_karaoke_bg_color = i_font_color;
1463 int i_font_size = p_sys->i_font_size;
1465 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1466 &i_font_color, &i_karaoke_bg_color ))
1468 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1469 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1474 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1475 bool b_uline, bool b_through, bool b_bold,
1476 bool b_italic, int i_karaoke_bgcolor,
1477 line_desc_t *p_line, uint32_t *psz_unicode,
1478 int *pi_pen_x, int i_pen_y, int *pi_start,
1479 FT_Vector *p_result )
1484 bool b_first_on_line = true;
1487 int i_pen_x_start = *pi_pen_x;
1489 uint32_t *psz_unicode_start = psz_unicode;
1491 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1493 /* Account for part of line already in position */
1494 for( i=0; i<*pi_start; i++ )
1498 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1499 ft_glyph_bbox_pixels, &glyph_size );
1501 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1502 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1503 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1504 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1510 b_first_on_line = false;
1512 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1518 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1519 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1523 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1524 ft_kerning_default, &delta );
1525 *pi_pen_x += delta.x >> 6;
1527 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1528 p_line->p_glyph_pos[ i ].y = i_pen_y;
1530 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1534 "unable to render text FT_Load_Glyph returned %d", i_error );
1535 p_line->pp_glyphs[ i ] = NULL;
1536 return VLC_EGENERIC;
1539 /* Do synthetic styling now that Freetype supports it;
1540 * ie. if the font we have loaded is NOT already in the
1541 * style that the tags want, then switch it on; if they
1542 * are then don't. */
1543 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1544 FT_GlyphSlot_Embolden( p_face->glyph );
1545 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1546 FT_GlyphSlot_Oblique( p_face->glyph );
1548 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1552 "unable to render text FT_Get_Glyph returned %d", i_error );
1553 p_line->pp_glyphs[ i ] = NULL;
1554 return VLC_EGENERIC;
1556 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1557 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1560 FT_Done_Glyph( tmp_glyph );
1563 if( b_uline || b_through )
1565 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1566 p_face->size->metrics.y_scale));
1567 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1568 p_face->size->metrics.y_scale));
1570 p_line->pi_underline_offset[ i ] =
1571 ( aOffset < 0 ) ? -aOffset : aOffset;
1572 p_line->pi_underline_thickness[ i ] =
1573 ( aSize < 0 ) ? -aSize : aSize;
1576 /* Move the baseline to make it strikethrough instead of
1577 * underline. That means that strikethrough takes precedence
1579 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1580 p_face->size->metrics.y_scale));
1582 p_line->pi_underline_offset[ i ] -=
1583 ( aDescent < 0 ) ? -aDescent : aDescent;
1587 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1588 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1589 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1590 p_line->p_fg_bg_ratio[ i ] = 0x00;
1592 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1593 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1594 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1596 for( ; i >= *pi_start; i-- )
1597 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1600 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1604 if( psz_unicode == psz_unicode_start )
1606 if( b_first_on_line )
1608 msg_Warn( p_filter, "unbreakable string" );
1609 p_line->pp_glyphs[ i ] = NULL;
1610 return VLC_EGENERIC;
1612 *pi_pen_x = i_pen_x_start;
1614 p_line->i_width = line.xMax;
1615 p_line->i_height = __MAX( p_line->i_height,
1616 p_face->size->metrics.height >> 6 );
1617 p_line->pp_glyphs[ i ] = NULL;
1619 p_result->x = __MAX( p_result->x, line.xMax );
1620 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1621 i_yMax - i_yMin ) );
1626 *psz_unicode = '\n';
1628 psz_unicode = psz_unicode_start;
1629 *pi_pen_x = i_pen_x_start;
1637 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1638 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1640 i_previous = i_glyph_index;
1641 *pi_pen_x += p_face->glyph->advance.x >> 6;
1644 p_line->i_width = line.xMax;
1645 p_line->i_height = __MAX( p_line->i_height,
1646 p_face->size->metrics.height >> 6 );
1647 p_line->pp_glyphs[ i ] = NULL;
1649 p_result->x = __MAX( p_result->x, line.xMax );
1650 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1651 line.yMax - line.yMin ) );
1655 /* Get rid of any text processed - if necessary repositioning
1656 * at the start of a new line of text
1660 *psz_unicode_start = '\0';
1662 else if( psz_unicode > psz_unicode_start )
1664 for( i=0; psz_unicode[ i ]; i++ )
1665 psz_unicode_start[ i ] = psz_unicode[ i ];
1666 psz_unicode_start[ i ] = '\0';
1672 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1673 uint32_t **psz_text_out, uint32_t *pi_runs,
1674 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1675 ft_style_t *p_style )
1677 uint32_t i_string_length = 0;
1679 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1680 *psz_text_out += i_string_length;
1682 if( ppp_styles && ppi_run_lengths )
1686 /* XXX this logic looks somewhat broken */
1690 *ppp_styles = realloc_or_free( *ppp_styles,
1691 *pi_runs * sizeof( ft_style_t * ) );
1693 else if( *pi_runs == 1 )
1695 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1698 /* We have just malloc'ed this memory successfully -
1699 * *pi_runs HAS to be within the memory area of *ppp_styles */
1702 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1706 /* XXX more iffy logic */
1708 if( *ppi_run_lengths )
1710 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1711 *pi_runs * sizeof( uint32_t ) );
1713 else if( *pi_runs == 1 )
1715 *ppi_run_lengths = (uint32_t *)
1716 malloc( *pi_runs * sizeof( uint32_t ) );
1719 /* same remarks here */
1720 if( *ppi_run_lengths )
1722 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1725 /* If we couldn't use the p_style argument due to memory allocation
1726 * problems above, release it here.
1728 if( p_style ) DeleteStyle( p_style );
1731 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1735 for( k=0; k < p_sys->i_font_attachments; k++ )
1737 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1739 FT_Face p_face = NULL;
1741 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1749 bool match = !strcasecmp( p_face->family_name,
1750 p_style->psz_fontname );
1752 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1753 match = match && p_style->b_bold;
1755 match = match && !p_style->b_bold;
1757 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1758 match = match && p_style->b_italic;
1760 match = match && !p_style->b_italic;
1768 FT_Done_Face( p_face );
1773 return VLC_EGENERIC;
1776 static int ProcessLines( filter_t *p_filter,
1781 uint32_t *pi_run_lengths,
1782 ft_style_t **pp_styles,
1783 line_desc_t **pp_lines,
1785 FT_Vector *p_result,
1789 uint32_t *pi_k_run_lengths,
1790 uint32_t *pi_k_durations )
1792 filter_sys_t *p_sys = p_filter->p_sys;
1793 ft_style_t **pp_char_styles;
1794 int *p_new_positions = NULL;
1795 int8_t *p_levels = NULL;
1796 uint8_t *pi_karaoke_bar = NULL;
1800 /* Assign each character in the text string its style explicitly, so that
1801 * after the characters have been shuffled around by Fribidi, we can re-apply
1802 * the styles, and to simplify the calculation of runs within a line.
1804 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1805 if( !pp_char_styles )
1810 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1811 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1812 * we just won't be able to display the progress bar; at least we'll
1818 for( j = 0; j < i_runs; j++ )
1819 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1820 pp_char_styles[ i++ ] = pp_styles[ j ];
1822 #if defined(HAVE_FRIBIDI)
1824 ft_style_t **pp_char_styles_new;
1825 int *p_old_positions;
1826 uint32_t *p_fribidi_string;
1827 int start_pos, pos = 0;
1829 pp_char_styles_new = (ft_style_t **)
1830 malloc( i_len * sizeof( ft_style_t * ));
1832 p_fribidi_string = (uint32_t *)
1833 malloc( (i_len + 1) * sizeof(uint32_t) );
1834 p_old_positions = (int *)
1835 malloc( (i_len + 1) * sizeof( int ) );
1836 p_new_positions = (int *)
1837 malloc( (i_len + 1) * sizeof( int ) );
1838 p_levels = (int8_t *)
1839 malloc( (i_len + 1) * sizeof( int8_t ) );
1841 if( ! pp_char_styles_new ||
1842 ! p_fribidi_string ||
1843 ! p_old_positions ||
1844 ! p_new_positions ||
1848 free( p_old_positions );
1849 free( p_new_positions );
1850 free( p_fribidi_string );
1851 free( pp_char_styles_new );
1852 free( pi_karaoke_bar );
1854 free( pp_char_styles );
1858 /* Do bidi conversion line-by-line */
1861 while(pos < i_len) {
1862 if (psz_text[pos] != '\n')
1864 p_fribidi_string[pos] = psz_text[pos];
1865 pp_char_styles_new[pos] = pp_char_styles[pos];
1866 p_new_positions[pos] = pos;
1871 while(pos < i_len) {
1872 if (psz_text[pos] == '\n')
1876 if (pos > start_pos)
1878 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1879 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1880 pos - start_pos, &base_dir,
1881 (FriBidiChar*)p_fribidi_string + start_pos,
1882 p_new_positions + start_pos,
1884 p_levels + start_pos );
1885 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1887 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1888 p_old_positions[ j - start_pos ] ];
1889 p_new_positions[ j ] += start_pos;
1893 free( p_old_positions );
1894 free( pp_char_styles );
1895 pp_char_styles = pp_char_styles_new;
1896 psz_text = p_fribidi_string;
1897 p_fribidi_string[ i_len ] = 0;
1900 /* Work out the karaoke */
1901 if( pi_karaoke_bar )
1903 int64_t i_last_duration = 0;
1904 int64_t i_duration = 0;
1905 int64_t i_start_pos = 0;
1906 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1908 for( k = 0; k< i_k_runs; k++ )
1910 double fraction = 0.0;
1912 i_duration += pi_k_durations[ k ];
1914 if( i_duration < i_elapsed )
1916 /* Completely finished this run-length -
1917 * let it render normally */
1921 else if( i_elapsed < i_last_duration )
1923 /* Haven't got up to this segment yet -
1924 * render it completely in karaoke BG mode */
1930 /* Partway through this run */
1932 fraction = (double)(i_elapsed - i_last_duration) /
1933 (double)pi_k_durations[ k ];
1935 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1937 double shade = pi_k_run_lengths[ k ] * fraction;
1939 if( p_new_positions )
1940 j = p_new_positions[ i_start_pos + i ];
1942 j = i_start_pos + i;
1944 if( i < (uint32_t)shade )
1945 pi_karaoke_bar[ j ] = 0xff;
1946 else if( (double)i > shade )
1947 pi_karaoke_bar[ j ] = 0x00;
1950 shade -= (int)shade;
1951 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1952 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1956 i_last_duration = i_duration;
1957 i_start_pos += pi_k_run_lengths[ k ];
1961 free( p_new_positions );
1963 FT_Vector tmp_result;
1965 line_desc_t *p_line = NULL;
1966 line_desc_t *p_prev = NULL;
1972 p_result->x = p_result->y = 0;
1973 tmp_result.x = tmp_result.y = 0;
1976 for( k = 0; k <= (uint32_t) i_len; k++ )
1978 if( ( k == (uint32_t) i_len ) ||
1980 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1982 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1984 /* End of the current style run */
1985 FT_Face p_face = NULL;
1988 /* Look for a match amongst our attachments first */
1989 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1993 char *psz_fontfile = NULL;
1995 psz_fontfile = FontConfig_Select( NULL,
1996 p_style->psz_fontname,
2000 if( psz_fontfile && ! *psz_fontfile )
2002 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2003 " so using default font", p_style->psz_fontname,
2004 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2005 (p_style->b_bold ? "(Bold)" :
2006 (p_style->b_italic ? "(Italic)" : ""))) );
2007 free( psz_fontfile );
2008 psz_fontfile = NULL;
2013 if( FT_New_Face( p_sys->p_library,
2014 psz_fontfile, i_idx, &p_face ) )
2016 free( psz_fontfile );
2017 free( pp_char_styles );
2018 #if defined(HAVE_FRIBIDI)
2021 free( pi_karaoke_bar );
2022 return VLC_EGENERIC;
2024 free( psz_fontfile );
2028 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2030 /* We've loaded a font face which is unhelpful for actually
2031 * rendering text - fallback to the default one.
2033 FT_Done_Face( p_face );
2037 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2038 ft_encoding_unicode ) ||
2039 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2040 p_style->i_font_size ) )
2042 if( p_face ) FT_Done_Face( p_face );
2043 free( pp_char_styles );
2044 #if defined(HAVE_FRIBIDI)
2047 free( pi_karaoke_bar );
2048 return VLC_EGENERIC;
2050 p_sys->i_use_kerning =
2051 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2054 uint32_t *psz_unicode = (uint32_t *)
2055 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2058 if( p_face ) FT_Done_Face( p_face );
2059 free( pp_char_styles );
2060 free( psz_unicode );
2061 #if defined(HAVE_FRIBIDI)
2064 free( pi_karaoke_bar );
2067 memcpy( psz_unicode, psz_text + i_prev,
2068 sizeof( uint32_t ) * ( k - i_prev ) );
2069 psz_unicode[ k - i_prev ] = 0;
2070 while( *psz_unicode )
2074 if( !(p_line = NewLine( i_len - i_prev)) )
2076 if( p_face ) FT_Done_Face( p_face );
2077 free( pp_char_styles );
2078 free( psz_unicode );
2079 #if defined(HAVE_FRIBIDI)
2082 free( pi_karaoke_bar );
2085 /* New Color mode only works in YUVA rendering mode --
2086 * (RGB mode has palette constraints on it). We therefore
2087 * need to populate the legacy colour fields also.
2089 p_line->b_new_color_mode = true;
2090 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2091 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2092 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2093 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2094 p_line->p_next = NULL;
2096 i_pen_y += tmp_result.y;
2100 if( p_prev ) p_prev->p_next = p_line;
2101 else *pp_lines = p_line;
2104 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2105 p_style->i_font_color, p_style->b_underline,
2109 p_style->i_karaoke_bg_color,
2110 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2111 &tmp_result ) != VLC_SUCCESS )
2113 if( p_face ) FT_Done_Face( p_face );
2114 free( pp_char_styles );
2115 free( psz_unicode );
2116 #if defined(HAVE_FRIBIDI)
2119 free( pi_karaoke_bar );
2120 return VLC_EGENERIC;
2125 p_result->x = __MAX( p_result->x, tmp_result.x );
2126 p_result->y += tmp_result.y;
2131 if( *psz_unicode == '\n')
2135 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2137 *c_ptr = *(c_ptr+1);
2142 free( psz_unicode );
2143 if( p_face ) FT_Done_Face( p_face );
2147 free( pp_char_styles );
2148 #if defined(HAVE_FRIBIDI)
2153 p_result->x = __MAX( p_result->x, tmp_result.x );
2154 p_result->y += tmp_result.y;
2157 if( pi_karaoke_bar )
2160 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2162 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2164 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2168 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2170 /* 100% BG colour will render faster if we
2171 * instead make it 100% FG colour, so leave
2172 * the ratio alone and copy the value across
2174 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2178 if( pi_karaoke_bar[ i ] & 0x80 )
2180 /* Swap Left and Right sides over for Right aligned
2181 * language text (eg. Arabic, Hebrew)
2183 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2185 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2186 p_line->p_bg_rgb[ k ] = i_tmp;
2188 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2191 /* Jump over the '\n' at the line-end */
2194 free( pi_karaoke_bar );
2200 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2201 subpicture_region_t *p_region_in )
2203 int rv = VLC_SUCCESS;
2204 stream_t *p_sub = NULL;
2205 xml_reader_t *p_xml_reader = NULL;
2207 if( !p_region_in || !p_region_in->psz_html )
2208 return VLC_EGENERIC;
2210 /* Reset the default fontsize in case screen metrics have changed */
2211 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2213 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2214 (uint8_t *) p_region_in->psz_html,
2215 strlen( p_region_in->psz_html ),
2219 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2220 if( p_filter->p_sys->p_xml )
2222 bool b_karaoke = false;
2224 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2227 /* Look for Root Node */
2228 if( xml_ReaderRead( p_xml_reader ) == 1 )
2230 char *psz_node = xml_ReaderName( p_xml_reader );
2232 if( !strcasecmp( "karaoke", psz_node ) )
2234 /* We're going to have to render the text a number
2235 * of times to show the progress marker on the text.
2237 var_SetBool( p_filter, "text-rerender", true );
2240 else if( !strcasecmp( "text", psz_node ) )
2246 /* Only text and karaoke tags are supported */
2247 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2248 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2249 p_xml_reader = NULL;
2261 uint32_t i_runs = 0;
2262 uint32_t i_k_runs = 0;
2263 uint32_t *pi_run_lengths = NULL;
2264 uint32_t *pi_k_run_lengths = NULL;
2265 uint32_t *pi_k_durations = NULL;
2266 ft_style_t **pp_styles = NULL;
2268 line_desc_t *p_lines = NULL;
2270 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2271 sizeof( uint32_t ) );
2276 rv = ProcessNodes( p_filter, p_xml_reader,
2277 p_region_in->p_style, psz_text, &i_len,
2278 &i_runs, &pi_run_lengths, &pp_styles,
2280 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2283 p_region_out->i_x = p_region_in->i_x;
2284 p_region_out->i_y = p_region_in->i_y;
2286 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2288 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2289 pi_run_lengths, pp_styles, &p_lines, &result,
2290 b_karaoke, i_k_runs, pi_k_run_lengths,
2294 for( k=0; k<i_runs; k++)
2295 DeleteStyle( pp_styles[k] );
2297 free( pi_run_lengths );
2300 /* Don't attempt to render text that couldn't be layed out
2303 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2305 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2307 Render( p_filter, p_region_out, p_lines,
2308 result.x, result.y );
2312 RenderYUVA( p_filter, p_region_out, p_lines,
2313 result.x, result.y );
2317 FreeLines( p_lines );
2319 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2322 stream_Delete( p_sub );
2328 static char* FontConfig_Select( FcConfig* priv, const char* family,
2329 bool b_bold, bool b_italic, int *i_idx )
2332 FcPattern *pat, *p_pat;
2336 pat = FcPatternCreate();
2337 if (!pat) return NULL;
2339 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2340 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2341 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2342 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2344 FcDefaultSubstitute( pat );
2346 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2348 FcPatternDestroy( pat );
2352 p_pat = FcFontMatch( priv, pat, &result );
2353 FcPatternDestroy( pat );
2354 if( !p_pat ) return NULL;
2356 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2357 || ( val_b != FcTrue ) )
2359 FcPatternDestroy( p_pat );
2362 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2367 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2369 FcPatternDestroy( p_pat );
2374 if( strcasecmp((const char*)val_s, family ) != 0 )
2375 msg_Warn( p_filter, "fontconfig: selected font family is not"
2376 "the requested one: '%s' != '%s'\n",
2377 (const char*)val_s, family );
2380 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2382 FcPatternDestroy( p_pat );
2386 FcPatternDestroy( p_pat );
2387 return strdup( (const char*)val_s );
2391 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2392 uint32_t **psz_text_out, uint32_t *pi_runs,
2393 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2394 ft_style_t *p_style )
2396 VLC_UNUSED(p_filter);
2397 VLC_UNUSED(psz_text_in);
2398 VLC_UNUSED(psz_text_out);
2399 VLC_UNUSED(pi_runs);
2400 VLC_UNUSED(ppi_run_lengths);
2401 VLC_UNUSED(ppp_styles);
2402 VLC_UNUSED(p_style);
2405 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2406 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2407 bool b_uline, bool b_through )
2410 VLC_UNUSED(p_fonts);
2412 VLC_UNUSED(b_italic);
2413 VLC_UNUSED(b_uline);
2414 VLC_UNUSED(b_through);
2419 static void FreeLine( line_desc_t *p_line )
2422 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2424 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2426 free( p_line->pp_glyphs );
2427 free( p_line->p_glyph_pos );
2428 free( p_line->p_fg_rgb );
2429 free( p_line->p_bg_rgb );
2430 free( p_line->p_fg_bg_ratio );
2431 free( p_line->pi_underline_offset );
2432 free( p_line->pi_underline_thickness );
2436 static void FreeLines( line_desc_t *p_lines )
2438 line_desc_t *p_line, *p_next;
2440 if( !p_lines ) return;
2442 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2444 p_next = p_line->p_next;
2449 static line_desc_t *NewLine( int i_count )
2451 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2453 if( !p_line ) return NULL;
2454 p_line->i_height = 0;
2455 p_line->i_width = 0;
2456 p_line->p_next = NULL;
2458 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2459 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2460 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2461 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2462 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2463 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2464 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2465 if( ( p_line->pp_glyphs == NULL ) ||
2466 ( p_line->p_glyph_pos == NULL ) ||
2467 ( p_line->p_fg_rgb == NULL ) ||
2468 ( p_line->p_bg_rgb == NULL ) ||
2469 ( p_line->p_fg_bg_ratio == NULL ) ||
2470 ( p_line->pi_underline_offset == NULL ) ||
2471 ( p_line->pi_underline_thickness == NULL ) )
2473 free( p_line->pi_underline_thickness );
2474 free( p_line->pi_underline_offset );
2475 free( p_line->p_fg_rgb );
2476 free( p_line->p_bg_rgb );
2477 free( p_line->p_fg_bg_ratio );
2478 free( p_line->p_glyph_pos );
2479 free( p_line->pp_glyphs );
2483 p_line->pp_glyphs[0] = NULL;
2484 p_line->b_new_color_mode = false;
2489 static int GetFontSize( filter_t *p_filter )
2491 filter_sys_t *p_sys = p_filter->p_sys;
2495 if( p_sys->i_default_font_size )
2497 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2498 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2500 i_size = p_sys->i_default_font_size;
2504 var_Get( p_filter, "freetype-rel-fontsize", &val );
2507 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2508 p_filter->p_sys->i_display_height =
2509 p_filter->fmt_out.video.i_height;
2514 msg_Warn( p_filter, "invalid fontsize, using 12" );
2515 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2516 i_size = 12 * val.i_int / 1000;
2523 static int SetFontSize( filter_t *p_filter, int i_size )
2525 filter_sys_t *p_sys = p_filter->p_sys;
2529 i_size = GetFontSize( p_filter );
2531 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2534 p_sys->i_font_size = i_size;
2536 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2538 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2539 return VLC_EGENERIC;
2545 static void YUVFromRGB( uint32_t i_argb,
2546 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2548 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2549 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2550 int i_blue = ( i_argb & 0x000000ff );
2552 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2553 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2554 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2555 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2556 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2557 -585 * i_blue + 4096 + 1048576) >> 13, 240);