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>
43 #include <vlc_charset.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, FONT_TEXT, FONT_LONGTEXT,
155 add_integer( "freetype-fontsize", 0, 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, COLOR_TEXT,
164 COLOR_LONGTEXT, false )
165 change_integer_list( pi_color_values, ppsz_color_descriptions )
167 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
168 FONTSIZER_LONGTEXT, false )
169 change_integer_list( pi_sizes, ppsz_sizes_text )
170 add_integer( "freetype-effect", 2, EFFECT_TEXT,
171 EFFECT_LONGTEXT, false )
172 change_integer_list( pi_effects, ppsz_effects_text )
174 add_bool( "freetype-yuvp", false, 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_InheritString( p_filter, "freetype-font" );
321 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
322 p_sys->i_effect = var_InheritInteger( p_filter, "freetype-effect" );
323 p_sys->i_font_opacity = var_InheritInteger( 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_InheritInteger( 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 );
486 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_ReaderDelete( 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_attachment_t **pp_attachments;
539 int i_attachments_cnt;
541 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
544 p_sys->i_font_attachments = 0;
545 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
546 if( !p_sys->pp_font_attachments )
549 for( int k = 0; k < i_attachments_cnt; k++ )
551 input_attachment_t *p_attach = pp_attachments[k];
553 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
554 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
555 p_attach->i_data > 0 && p_attach->p_data )
557 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
561 vlc_input_attachment_Delete( p_attach );
564 free( pp_attachments );
569 /*****************************************************************************
570 * Render: place string in picture
571 *****************************************************************************
572 * This function merges the previously rendered freetype glyphs into a picture
573 *****************************************************************************/
574 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
575 line_desc_t *p_line, int i_width, int i_height )
577 VLC_UNUSED(p_filter);
578 static const uint8_t pi_gamma[16] =
579 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
580 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
584 int i, x, y, i_pitch;
585 uint8_t i_y; /* YUV values, derived from incoming RGB */
588 /* Create a new subpicture region */
589 memset( &fmt, 0, sizeof(video_format_t) );
590 fmt.i_chroma = VLC_CODEC_YUVP;
591 fmt.i_width = fmt.i_visible_width = i_width + 4;
592 fmt.i_height = fmt.i_visible_height = i_height + 4;
593 if( p_region->fmt.i_visible_width > 0 )
594 fmt.i_visible_width = p_region->fmt.i_visible_width;
595 if( p_region->fmt.i_visible_height > 0 )
596 fmt.i_visible_height = p_region->fmt.i_visible_height;
597 fmt.i_x_offset = fmt.i_y_offset = 0;
599 assert( !p_region->p_picture );
600 p_region->p_picture = picture_NewFromFormat( &fmt );
601 if( !p_region->p_picture )
603 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
606 /* Calculate text color components */
607 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
608 25 * p_line->i_blue + 128) >> 8) + 16;
609 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
610 112 * p_line->i_blue + 128) >> 8) + 128;
611 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
612 18 * p_line->i_blue + 128) >> 8) + 128;
615 fmt.p_palette->i_entries = 16;
616 for( i = 0; i < 8; i++ )
618 fmt.p_palette->palette[i][0] = 0;
619 fmt.p_palette->palette[i][1] = 0x80;
620 fmt.p_palette->palette[i][2] = 0x80;
621 fmt.p_palette->palette[i][3] = pi_gamma[i];
622 fmt.p_palette->palette[i][3] =
623 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
625 for( i = 8; i < fmt.p_palette->i_entries; i++ )
627 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
628 fmt.p_palette->palette[i][1] = i_u;
629 fmt.p_palette->palette[i][2] = i_v;
630 fmt.p_palette->palette[i][3] = pi_gamma[i];
631 fmt.p_palette->palette[i][3] =
632 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
635 p_dst = p_region->p_picture->Y_PIXELS;
636 i_pitch = p_region->p_picture->Y_PITCH;
638 /* Initialize the region pixels */
639 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
641 for( ; p_line != NULL; p_line = p_line->p_next )
643 int i_glyph_tmax = 0;
644 int i_bitmap_offset, i_offset, i_align_offset = 0;
645 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
647 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
648 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
651 if( p_line->i_width < i_width )
653 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
655 i_align_offset = i_width - p_line->i_width;
657 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
659 i_align_offset = ( i_width - p_line->i_width ) / 2;
663 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
665 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
667 i_offset = ( p_line->p_glyph_pos[ i ].y +
668 i_glyph_tmax - p_glyph->top + 2 ) *
669 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
672 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
674 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
676 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
678 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
685 /* Outlining (find something better than nearest neighbour filtering ?) */
688 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
689 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
690 uint8_t left, current;
692 for( y = 1; y < (int)fmt.i_height - 1; y++ )
694 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
695 p_dst += p_region->p_picture->Y_PITCH;
698 for( x = 1; x < (int)fmt.i_width - 1; x++ )
701 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
702 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;
706 memset( p_top, 0, fmt.i_width );
712 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
713 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
714 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
715 int i_glyph_tmax, int i_align_offset,
716 uint8_t i_y, uint8_t i_u, uint8_t i_v,
717 subpicture_region_t *p_region)
721 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
723 p_dst_y = p_region->p_picture->Y_PIXELS;
724 p_dst_u = p_region->p_picture->U_PIXELS;
725 p_dst_v = p_region->p_picture->V_PIXELS;
726 p_dst_a = p_region->p_picture->A_PIXELS;
727 i_pitch = p_region->p_picture->A_PITCH;
729 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
730 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
732 for( y = 0; y < i_line_thickness; y++ )
734 int i_extra = p_this_glyph->bitmap.width;
738 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
739 (p_this_glyph_pos->x + p_this_glyph->left);
741 for( x = 0; x < i_extra; x++ )
745 /* break the underline around the tails of any glyphs which cross it */
746 /* Strikethrough doesn't get broken */
747 for( z = x - i_line_thickness;
748 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
751 if( p_next_glyph && ( z >= i_extra ) )
753 int i_row = i_line_offset + p_next_glyph->top + y;
755 if( ( p_next_glyph->bitmap.rows > i_row ) &&
756 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
761 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
763 int i_row = i_line_offset + p_this_glyph->top + y;
765 if( ( p_this_glyph->bitmap.rows > i_row ) &&
766 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
775 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
776 p_dst_u[i_offset+x] = i_u;
777 p_dst_v[i_offset+x] = i_v;
778 p_dst_a[i_offset+x] = 255;
785 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
787 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
788 int i_pitch = p_region->p_picture->A_PITCH;
791 for( ; p_line != NULL; p_line = p_line->p_next )
793 int i_glyph_tmax=0, i = 0;
794 int i_bitmap_offset, i_offset, i_align_offset = 0;
795 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
797 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
798 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
801 if( p_line->i_width < i_width )
803 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
805 i_align_offset = i_width - p_line->i_width;
807 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
809 i_align_offset = ( i_width - p_line->i_width ) / 2;
813 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
815 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
817 i_offset = ( p_line->p_glyph_pos[ i ].y +
818 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
819 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
820 i_align_offset +xoffset;
822 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
824 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
826 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
827 if( p_dst[i_offset+x] <
828 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
830 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
839 /*****************************************************************************
840 * Render: place string in picture
841 *****************************************************************************
842 * This function merges the previously rendered freetype glyphs into a picture
843 *****************************************************************************/
844 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
845 line_desc_t *p_line, int i_width, int i_height )
847 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
849 int i, x, y, i_pitch, i_alpha;
850 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
852 if( i_width == 0 || i_height == 0 )
855 /* Create a new subpicture region */
856 memset( &fmt, 0, sizeof(video_format_t) );
857 fmt.i_chroma = VLC_CODEC_YUVA;
858 fmt.i_width = fmt.i_visible_width = i_width + 6;
859 fmt.i_height = fmt.i_visible_height = i_height + 6;
860 if( p_region->fmt.i_visible_width > 0 )
861 fmt.i_visible_width = p_region->fmt.i_visible_width;
862 if( p_region->fmt.i_visible_height > 0 )
863 fmt.i_visible_height = p_region->fmt.i_visible_height;
864 fmt.i_x_offset = fmt.i_y_offset = 0;
866 p_region->p_picture = picture_NewFromFormat( &fmt );
867 if( !p_region->p_picture )
871 /* Calculate text color components */
872 YUVFromRGB( (p_line->i_red << 16) |
873 (p_line->i_green << 8) |
876 i_alpha = p_line->i_alpha;
878 p_dst_y = p_region->p_picture->Y_PIXELS;
879 p_dst_u = p_region->p_picture->U_PIXELS;
880 p_dst_v = p_region->p_picture->V_PIXELS;
881 p_dst_a = p_region->p_picture->A_PIXELS;
882 i_pitch = p_region->p_picture->A_PITCH;
884 /* Initialize the region pixels */
885 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
887 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
888 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
889 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
890 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
894 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
895 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
896 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
897 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
899 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
900 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
902 DrawBlack( p_line, i_width, p_region, 0, 0);
903 DrawBlack( p_line, i_width, p_region, -1, 0);
904 DrawBlack( p_line, i_width, p_region, 0, -1);
905 DrawBlack( p_line, i_width, p_region, 1, 0);
906 DrawBlack( p_line, i_width, p_region, 0, 1);
909 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
911 DrawBlack( p_line, i_width, p_region, -1, -1);
912 DrawBlack( p_line, i_width, p_region, -1, 1);
913 DrawBlack( p_line, i_width, p_region, 1, -1);
914 DrawBlack( p_line, i_width, p_region, 1, 1);
916 DrawBlack( p_line, i_width, p_region, -2, 0);
917 DrawBlack( p_line, i_width, p_region, 0, -2);
918 DrawBlack( p_line, i_width, p_region, 2, 0);
919 DrawBlack( p_line, i_width, p_region, 0, 2);
921 DrawBlack( p_line, i_width, p_region, -2, -2);
922 DrawBlack( p_line, i_width, p_region, -2, 2);
923 DrawBlack( p_line, i_width, p_region, 2, -2);
924 DrawBlack( p_line, i_width, p_region, 2, 2);
926 DrawBlack( p_line, i_width, p_region, -3, 0);
927 DrawBlack( p_line, i_width, p_region, 0, -3);
928 DrawBlack( p_line, i_width, p_region, 3, 0);
929 DrawBlack( p_line, i_width, p_region, 0, 3);
932 for( ; p_line != NULL; p_line = p_line->p_next )
934 int i_glyph_tmax = 0;
935 int i_bitmap_offset, i_offset, i_align_offset = 0;
936 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
938 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
939 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
942 if( p_line->i_width < i_width )
944 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
946 i_align_offset = i_width - p_line->i_width;
948 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
950 i_align_offset = ( i_width - p_line->i_width ) / 2;
954 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
956 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
958 i_offset = ( p_line->p_glyph_pos[ i ].y +
959 i_glyph_tmax - p_glyph->top + 3 ) *
960 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
963 if( p_line->b_new_color_mode )
965 /* Every glyph can (and in fact must) have its own color */
966 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
969 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
971 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
973 uint8_t i_y_local = i_y;
974 uint8_t i_u_local = i_u;
975 uint8_t i_v_local = i_v;
977 if( p_line->p_fg_bg_ratio != 0x00 )
979 int i_split = p_glyph->bitmap.width *
980 p_line->p_fg_bg_ratio[ i ] / 0x7f;
984 YUVFromRGB( p_line->p_bg_rgb[ i ],
985 &i_y_local, &i_u_local, &i_v_local );
989 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
991 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
992 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
994 p_dst_u[i_offset+x] = i_u;
995 p_dst_v[i_offset+x] = i_v;
997 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
998 p_dst_a[i_offset+x] = 0xff;
1001 i_offset += i_pitch;
1004 if( p_line->pi_underline_thickness[ i ] )
1006 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1007 p_line->pi_underline_offset[ i ],
1008 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1009 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1010 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1011 i_glyph_tmax, i_align_offset,
1018 /* Apply the alpha setting */
1019 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1020 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1026 * This function renders a text subpicture region into another one.
1027 * It also calculates the size needed for this string, and renders the
1028 * needed glyphs into memory. It is used as pf_add_string callback in
1029 * the vout method by this module
1031 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1032 subpicture_region_t *p_region_in )
1034 filter_sys_t *p_sys = p_filter->p_sys;
1035 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1036 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1037 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1038 size_t i_string_length;
1040 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1050 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1051 psz_string = p_region_in->psz_text;
1052 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1054 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1055 i_scale = val.i_int;
1057 if( p_region_in->p_style )
1059 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1060 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1061 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1065 i_font_color = p_sys->i_font_color;
1066 i_font_alpha = 255 - p_sys->i_font_opacity;
1067 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1070 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1071 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1072 SetFontSize( p_filter, i_font_size );
1074 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1075 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1076 i_blue = i_font_color & 0x000000FF;
1078 result.x = result.y = 0;
1079 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1081 #if defined(WORDS_BIGENDIAN)
1082 psz_unicode = ToCharset( "UCS-4BE", psz_string, &i_string_length );
1084 psz_unicode = ToCharset( "UCS-4LE", psz_string, &i_string_length );
1086 if( psz_unicode == NULL )
1088 psz_unicode_orig = psz_unicode;
1089 i_string_length /= 4;
1091 #if defined(HAVE_FRIBIDI)
1093 uint32_t *p_fribidi_string;
1094 int32_t start_pos, pos = 0;
1096 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1097 if( !p_fribidi_string )
1100 /* Do bidi conversion line-by-line */
1101 while( pos < i_string_length )
1103 while( pos < i_string_length )
1105 i_char = psz_unicode[pos];
1106 if (i_char != '\r' && i_char != '\n')
1108 p_fribidi_string[pos] = i_char;
1112 while( pos < i_string_length )
1114 i_char = psz_unicode[pos];
1115 if (i_char == '\r' || i_char == '\n')
1119 if (pos > start_pos)
1121 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1122 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1125 (FriBidiChar*)p_fribidi_string + start_pos,
1130 free( psz_unicode_orig );
1131 psz_unicode = psz_unicode_orig = p_fribidi_string;
1132 p_fribidi_string[ i_string_length ] = 0;
1136 /* Calculate relative glyph positions and a bounding box for the
1138 if( !(p_line = NewLine( strlen( psz_string ))) )
1141 i_pen_x = i_pen_y = 0;
1143 psz_line_start = psz_unicode;
1145 #define face p_sys->p_face
1146 #define glyph face->glyph
1148 while( *psz_unicode )
1150 i_char = *psz_unicode++;
1151 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1156 if( i_char == '\n' )
1158 psz_line_start = psz_unicode;
1159 if( !(p_next = NewLine( strlen( psz_string ))) )
1161 p_line->p_next = p_next;
1162 p_line->i_width = line.xMax;
1163 p_line->i_height = face->size->metrics.height >> 6;
1164 p_line->pp_glyphs[ i ] = NULL;
1165 p_line->i_alpha = i_font_alpha;
1166 p_line->i_red = i_red;
1167 p_line->i_green = i_green;
1168 p_line->i_blue = i_blue;
1171 result.x = __MAX( result.x, line.xMax );
1172 result.y += face->size->metrics.height >> 6;
1175 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1176 i_pen_y += face->size->metrics.height >> 6;
1178 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1183 i_glyph_index = FT_Get_Char_Index( face, i_char );
1184 if( p_sys->i_use_kerning && i_glyph_index
1188 FT_Get_Kerning( face, i_previous, i_glyph_index,
1189 ft_kerning_default, &delta );
1190 i_pen_x += delta.x >> 6;
1193 p_line->p_glyph_pos[ i ].x = i_pen_x;
1194 p_line->p_glyph_pos[ i ].y = i_pen_y;
1195 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1198 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1201 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1206 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1209 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1213 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1214 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1217 FT_Done_Glyph( tmp_glyph );
1220 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1223 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1224 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1225 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1227 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1228 p_line->pp_glyphs[ i ] = NULL;
1230 p_line = NewLine( strlen( psz_string ));
1231 if( p_prev ) p_prev->p_next = p_line;
1232 else p_lines = p_line;
1234 uint32_t *psz_unicode_saved = psz_unicode;
1235 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1239 if( psz_unicode == psz_line_start )
1240 { /* try harder to break that line */
1241 psz_unicode = psz_unicode_saved;
1242 while( psz_unicode > psz_line_start &&
1243 *psz_unicode != '_' && *psz_unicode != '/' &&
1244 *psz_unicode != '\\' && *psz_unicode != '.' )
1249 if( psz_unicode == psz_line_start )
1251 msg_Warn( p_filter, "unbreakable string" );
1256 *psz_unicode = '\n';
1258 psz_unicode = psz_line_start;
1261 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1264 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1265 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1267 i_previous = i_glyph_index;
1268 i_pen_x += glyph->advance.x >> 6;
1272 p_line->i_width = line.xMax;
1273 p_line->i_height = face->size->metrics.height >> 6;
1274 p_line->pp_glyphs[ i ] = NULL;
1275 p_line->i_alpha = i_font_alpha;
1276 p_line->i_red = i_red;
1277 p_line->i_green = i_green;
1278 p_line->i_blue = i_blue;
1279 result.x = __MAX( result.x, line.xMax );
1280 result.y += line.yMax - line.yMin;
1285 p_region_out->i_x = p_region_in->i_x;
1286 p_region_out->i_y = p_region_in->i_y;
1288 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1289 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1291 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1293 free( psz_unicode_orig );
1294 FreeLines( p_lines );
1298 free( psz_unicode_orig );
1299 FreeLines( p_lines );
1300 return VLC_EGENERIC;
1303 #ifdef HAVE_FONTCONFIG
1304 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1305 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1306 bool b_italic, bool b_uline, bool b_through )
1308 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1312 p_style->i_font_size = i_font_size;
1313 p_style->i_font_color = i_font_color;
1314 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1315 p_style->b_italic = b_italic;
1316 p_style->b_bold = b_bold;
1317 p_style->b_underline = b_uline;
1318 p_style->b_through = b_through;
1320 p_style->psz_fontname = strdup( psz_fontname );
1325 static void DeleteStyle( ft_style_t *p_style )
1329 free( p_style->psz_fontname );
1334 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1341 if(( s1->i_font_size == s2->i_font_size ) &&
1342 ( s1->i_font_color == s2->i_font_color ) &&
1343 ( s1->b_italic == s2->b_italic ) &&
1344 ( s1->b_through == s2->b_through ) &&
1345 ( s1->b_bold == s2->b_bold ) &&
1346 ( s1->b_underline == s2->b_underline ) &&
1347 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1354 static void IconvText( filter_t *p_filter, const char *psz_string,
1355 size_t *i_string_length, uint32_t **ppsz_unicode )
1357 /* If memory hasn't been allocated for our output string, allocate it here
1358 * - the calling function must now be responsible for freeing it.
1360 if( !*ppsz_unicode )
1361 *ppsz_unicode = (uint32_t *)
1362 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1364 /* We don't need to handle a NULL pointer in *ppsz_unicode
1365 * if we are instead testing for a non NULL value like we are here */
1370 #if defined(WORDS_BIGENDIAN)
1371 ToCharset( "UCS-4BE", psz_string, i_string_length );
1373 ToCharset( "UCS-4LE", psz_string, i_string_length );
1375 if( *ppsz_unicode != NULL )
1376 *i_string_length /= 4;
1378 /* FIXME: This is going to fail miserably in the caller */
1379 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1383 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1384 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1385 bool b_uline, bool b_through )
1387 ft_style_t *p_style = NULL;
1389 char *psz_fontname = NULL;
1390 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1391 uint32_t i_karaoke_bg_color = i_font_color;
1392 int i_font_size = p_sys->i_font_size;
1394 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1395 &i_font_color, &i_karaoke_bg_color ))
1397 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1398 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1403 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1404 bool b_uline, bool b_through, bool b_bold,
1405 bool b_italic, int i_karaoke_bgcolor,
1406 line_desc_t *p_line, uint32_t *psz_unicode,
1407 int *pi_pen_x, int i_pen_y, int *pi_start,
1408 FT_Vector *p_result )
1413 bool b_first_on_line = true;
1416 int i_pen_x_start = *pi_pen_x;
1418 uint32_t *psz_unicode_start = psz_unicode;
1420 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1422 /* Account for part of line already in position */
1423 for( i=0; i<*pi_start; i++ )
1427 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1428 ft_glyph_bbox_pixels, &glyph_size );
1430 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1431 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1432 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1433 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1439 b_first_on_line = false;
1441 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1447 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1448 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1452 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1453 ft_kerning_default, &delta );
1454 *pi_pen_x += delta.x >> 6;
1456 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1457 p_line->p_glyph_pos[ i ].y = i_pen_y;
1459 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1462 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1466 "unable to render text FT_Load_Glyph returned %d", i_error );
1467 p_line->pp_glyphs[ i ] = NULL;
1468 return VLC_EGENERIC;
1472 /* Do synthetic styling now that Freetype supports it;
1473 * ie. if the font we have loaded is NOT already in the
1474 * style that the tags want, then switch it on; if they
1475 * are then don't. */
1476 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1477 FT_GlyphSlot_Embolden( p_face->glyph );
1478 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1479 FT_GlyphSlot_Oblique( p_face->glyph );
1481 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1485 "unable to render text FT_Get_Glyph returned %d", i_error );
1486 p_line->pp_glyphs[ i ] = NULL;
1487 return VLC_EGENERIC;
1489 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1490 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1493 FT_Done_Glyph( tmp_glyph );
1496 if( b_uline || b_through )
1498 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1499 p_face->size->metrics.y_scale));
1500 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1501 p_face->size->metrics.y_scale));
1503 p_line->pi_underline_offset[ i ] =
1504 ( aOffset < 0 ) ? -aOffset : aOffset;
1505 p_line->pi_underline_thickness[ i ] =
1506 ( aSize < 0 ) ? -aSize : aSize;
1509 /* Move the baseline to make it strikethrough instead of
1510 * underline. That means that strikethrough takes precedence
1512 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1513 p_face->size->metrics.y_scale));
1515 p_line->pi_underline_offset[ i ] -=
1516 ( aDescent < 0 ) ? -aDescent : aDescent;
1520 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1521 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1522 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1523 p_line->p_fg_bg_ratio[ i ] = 0x00;
1525 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1526 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1527 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1529 for( ; i >= *pi_start; i-- )
1530 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1533 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1537 if( psz_unicode == psz_unicode_start )
1539 if( b_first_on_line )
1541 msg_Warn( p_filter, "unbreakable string" );
1542 p_line->pp_glyphs[ i ] = NULL;
1543 return VLC_EGENERIC;
1545 *pi_pen_x = i_pen_x_start;
1547 p_line->i_width = line.xMax;
1548 p_line->i_height = __MAX( p_line->i_height,
1549 p_face->size->metrics.height >> 6 );
1550 p_line->pp_glyphs[ i ] = NULL;
1552 p_result->x = __MAX( p_result->x, line.xMax );
1553 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1554 i_yMax - i_yMin ) );
1559 *psz_unicode = '\n';
1561 psz_unicode = psz_unicode_start;
1562 *pi_pen_x = i_pen_x_start;
1570 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1571 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1573 i_previous = i_glyph_index;
1574 *pi_pen_x += p_face->glyph->advance.x >> 6;
1577 p_line->i_width = line.xMax;
1578 p_line->i_height = __MAX( p_line->i_height,
1579 p_face->size->metrics.height >> 6 );
1580 p_line->pp_glyphs[ i ] = NULL;
1582 p_result->x = __MAX( p_result->x, line.xMax );
1583 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1584 line.yMax - line.yMin ) );
1588 /* Get rid of any text processed - if necessary repositioning
1589 * at the start of a new line of text
1593 *psz_unicode_start = '\0';
1595 else if( psz_unicode > psz_unicode_start )
1597 for( i=0; psz_unicode[ i ]; i++ )
1598 psz_unicode_start[ i ] = psz_unicode[ i ];
1599 psz_unicode_start[ i ] = '\0';
1605 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1606 uint32_t **psz_text_out, uint32_t *pi_runs,
1607 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1608 ft_style_t *p_style )
1610 size_t i_string_length;
1612 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1613 *psz_text_out += i_string_length;
1615 if( ppp_styles && ppi_run_lengths )
1619 /* XXX this logic looks somewhat broken */
1623 *ppp_styles = realloc_or_free( *ppp_styles,
1624 *pi_runs * sizeof( ft_style_t * ) );
1626 else if( *pi_runs == 1 )
1628 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1631 /* We have just malloc'ed this memory successfully -
1632 * *pi_runs HAS to be within the memory area of *ppp_styles */
1635 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1639 /* XXX more iffy logic */
1641 if( *ppi_run_lengths )
1643 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1644 *pi_runs * sizeof( uint32_t ) );
1646 else if( *pi_runs == 1 )
1648 *ppi_run_lengths = (uint32_t *)
1649 malloc( *pi_runs * sizeof( uint32_t ) );
1652 /* same remarks here */
1653 if( *ppi_run_lengths )
1655 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1658 /* If we couldn't use the p_style argument due to memory allocation
1659 * problems above, release it here.
1661 if( p_style ) DeleteStyle( p_style );
1664 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1668 for( k=0; k < p_sys->i_font_attachments; k++ )
1670 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1672 FT_Face p_face = NULL;
1674 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1682 bool match = !strcasecmp( p_face->family_name,
1683 p_style->psz_fontname );
1685 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1686 match = match && p_style->b_bold;
1688 match = match && !p_style->b_bold;
1690 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1691 match = match && p_style->b_italic;
1693 match = match && !p_style->b_italic;
1701 FT_Done_Face( p_face );
1706 return VLC_EGENERIC;
1709 static int ProcessLines( filter_t *p_filter,
1714 uint32_t *pi_run_lengths,
1715 ft_style_t **pp_styles,
1716 line_desc_t **pp_lines,
1718 FT_Vector *p_result,
1722 uint32_t *pi_k_run_lengths,
1723 uint32_t *pi_k_durations )
1725 filter_sys_t *p_sys = p_filter->p_sys;
1726 ft_style_t **pp_char_styles;
1727 int *p_new_positions = NULL;
1728 int8_t *p_levels = NULL;
1729 uint8_t *pi_karaoke_bar = NULL;
1733 /* Assign each character in the text string its style explicitly, so that
1734 * after the characters have been shuffled around by Fribidi, we can re-apply
1735 * the styles, and to simplify the calculation of runs within a line.
1737 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1738 if( !pp_char_styles )
1743 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1744 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1745 * we just won't be able to display the progress bar; at least we'll
1751 for( j = 0; j < i_runs; j++ )
1752 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1753 pp_char_styles[ i++ ] = pp_styles[ j ];
1755 #if defined(HAVE_FRIBIDI)
1757 ft_style_t **pp_char_styles_new;
1758 int *p_old_positions;
1759 uint32_t *p_fribidi_string;
1760 int start_pos, pos = 0;
1762 pp_char_styles_new = (ft_style_t **)
1763 malloc( i_len * sizeof( ft_style_t * ));
1765 p_fribidi_string = (uint32_t *)
1766 malloc( (i_len + 1) * sizeof(uint32_t) );
1767 p_old_positions = (int *)
1768 malloc( (i_len + 1) * sizeof( int ) );
1769 p_new_positions = (int *)
1770 malloc( (i_len + 1) * sizeof( int ) );
1771 p_levels = (int8_t *)
1772 malloc( (i_len + 1) * sizeof( int8_t ) );
1774 if( ! pp_char_styles_new ||
1775 ! p_fribidi_string ||
1776 ! p_old_positions ||
1777 ! p_new_positions ||
1781 free( p_old_positions );
1782 free( p_new_positions );
1783 free( p_fribidi_string );
1784 free( pp_char_styles_new );
1785 free( pi_karaoke_bar );
1787 free( pp_char_styles );
1791 /* Do bidi conversion line-by-line */
1794 while(pos < i_len) {
1795 if (psz_text[pos] != '\n')
1797 p_fribidi_string[pos] = psz_text[pos];
1798 pp_char_styles_new[pos] = pp_char_styles[pos];
1799 p_new_positions[pos] = pos;
1804 while(pos < i_len) {
1805 if (psz_text[pos] == '\n')
1809 if (pos > start_pos)
1811 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1812 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1813 pos - start_pos, &base_dir,
1814 (FriBidiChar*)p_fribidi_string + start_pos,
1815 p_new_positions + start_pos,
1817 p_levels + start_pos );
1818 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1820 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1821 p_old_positions[ j - start_pos ] ];
1822 p_new_positions[ j ] += start_pos;
1826 free( p_old_positions );
1827 free( pp_char_styles );
1828 pp_char_styles = pp_char_styles_new;
1829 psz_text = p_fribidi_string;
1830 p_fribidi_string[ i_len ] = 0;
1833 /* Work out the karaoke */
1834 if( pi_karaoke_bar )
1836 int64_t i_last_duration = 0;
1837 int64_t i_duration = 0;
1838 int64_t i_start_pos = 0;
1839 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1841 for( k = 0; k< i_k_runs; k++ )
1843 double fraction = 0.0;
1845 i_duration += pi_k_durations[ k ];
1847 if( i_duration < i_elapsed )
1849 /* Completely finished this run-length -
1850 * let it render normally */
1854 else if( i_elapsed < i_last_duration )
1856 /* Haven't got up to this segment yet -
1857 * render it completely in karaoke BG mode */
1863 /* Partway through this run */
1865 fraction = (double)(i_elapsed - i_last_duration) /
1866 (double)pi_k_durations[ k ];
1868 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1870 double shade = pi_k_run_lengths[ k ] * fraction;
1872 if( p_new_positions )
1873 j = p_new_positions[ i_start_pos + i ];
1875 j = i_start_pos + i;
1877 if( i < (uint32_t)shade )
1878 pi_karaoke_bar[ j ] = 0xff;
1879 else if( (double)i > shade )
1880 pi_karaoke_bar[ j ] = 0x00;
1883 shade -= (int)shade;
1884 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1885 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1889 i_last_duration = i_duration;
1890 i_start_pos += pi_k_run_lengths[ k ];
1894 free( p_new_positions );
1896 FT_Vector tmp_result;
1898 line_desc_t *p_line = NULL;
1899 line_desc_t *p_prev = NULL;
1905 p_result->x = p_result->y = 0;
1906 tmp_result.x = tmp_result.y = 0;
1909 for( k = 0; k <= (uint32_t) i_len; k++ )
1911 if( ( k == (uint32_t) i_len ) ||
1913 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1915 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1917 /* End of the current style run */
1918 FT_Face p_face = NULL;
1921 /* Look for a match amongst our attachments first */
1922 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1926 char *psz_fontfile = NULL;
1928 psz_fontfile = FontConfig_Select( NULL,
1929 p_style->psz_fontname,
1933 if( psz_fontfile && ! *psz_fontfile )
1935 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1936 " so using default font", p_style->psz_fontname,
1937 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1938 (p_style->b_bold ? "(Bold)" :
1939 (p_style->b_italic ? "(Italic)" : ""))) );
1940 free( psz_fontfile );
1941 psz_fontfile = NULL;
1946 if( FT_New_Face( p_sys->p_library,
1947 psz_fontfile, i_idx, &p_face ) )
1949 free( psz_fontfile );
1950 free( pp_char_styles );
1951 #if defined(HAVE_FRIBIDI)
1954 free( pi_karaoke_bar );
1955 return VLC_EGENERIC;
1957 free( psz_fontfile );
1961 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1963 /* We've loaded a font face which is unhelpful for actually
1964 * rendering text - fallback to the default one.
1966 FT_Done_Face( p_face );
1970 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1971 ft_encoding_unicode ) ||
1972 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1973 p_style->i_font_size ) )
1975 if( p_face ) FT_Done_Face( p_face );
1976 free( pp_char_styles );
1977 #if defined(HAVE_FRIBIDI)
1980 free( pi_karaoke_bar );
1981 return VLC_EGENERIC;
1983 p_sys->i_use_kerning =
1984 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1987 uint32_t *psz_unicode = (uint32_t *)
1988 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1991 if( p_face ) FT_Done_Face( p_face );
1992 free( pp_char_styles );
1993 free( psz_unicode );
1994 #if defined(HAVE_FRIBIDI)
1997 free( pi_karaoke_bar );
2000 memcpy( psz_unicode, psz_text + i_prev,
2001 sizeof( uint32_t ) * ( k - i_prev ) );
2002 psz_unicode[ k - i_prev ] = 0;
2003 while( *psz_unicode )
2007 if( !(p_line = NewLine( i_len - i_prev)) )
2009 if( p_face ) FT_Done_Face( p_face );
2010 free( pp_char_styles );
2011 free( psz_unicode );
2012 #if defined(HAVE_FRIBIDI)
2015 free( pi_karaoke_bar );
2018 /* New Color mode only works in YUVA rendering mode --
2019 * (RGB mode has palette constraints on it). We therefore
2020 * need to populate the legacy colour fields also.
2022 p_line->b_new_color_mode = true;
2023 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2024 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2025 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2026 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2027 p_line->p_next = NULL;
2029 i_pen_y += tmp_result.y;
2033 if( p_prev ) p_prev->p_next = p_line;
2034 else *pp_lines = p_line;
2037 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2038 p_style->i_font_color, p_style->b_underline,
2042 p_style->i_karaoke_bg_color,
2043 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2044 &tmp_result ) != VLC_SUCCESS )
2046 if( p_face ) FT_Done_Face( p_face );
2047 free( pp_char_styles );
2048 free( psz_unicode );
2049 #if defined(HAVE_FRIBIDI)
2052 free( pi_karaoke_bar );
2053 return VLC_EGENERIC;
2058 p_result->x = __MAX( p_result->x, tmp_result.x );
2059 p_result->y += tmp_result.y;
2064 if( *psz_unicode == '\n')
2068 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2070 *c_ptr = *(c_ptr+1);
2075 free( psz_unicode );
2076 if( p_face ) FT_Done_Face( p_face );
2080 free( pp_char_styles );
2081 #if defined(HAVE_FRIBIDI)
2086 p_result->x = __MAX( p_result->x, tmp_result.x );
2087 p_result->y += tmp_result.y;
2090 if( pi_karaoke_bar )
2093 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2095 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2097 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2101 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2103 /* 100% BG colour will render faster if we
2104 * instead make it 100% FG colour, so leave
2105 * the ratio alone and copy the value across
2107 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2111 if( pi_karaoke_bar[ i ] & 0x80 )
2113 /* Swap Left and Right sides over for Right aligned
2114 * language text (eg. Arabic, Hebrew)
2116 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2118 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2119 p_line->p_bg_rgb[ k ] = i_tmp;
2121 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2124 /* Jump over the '\n' at the line-end */
2127 free( pi_karaoke_bar );
2133 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2134 subpicture_region_t *p_region_in )
2136 int rv = VLC_SUCCESS;
2137 stream_t *p_sub = NULL;
2139 if( !p_region_in || !p_region_in->psz_html )
2140 return VLC_EGENERIC;
2142 /* Reset the default fontsize in case screen metrics have changed */
2143 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2145 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2146 (uint8_t *) p_region_in->psz_html,
2147 strlen( p_region_in->psz_html ),
2149 if( unlikely(p_sub == NULL) )
2152 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2153 bool b_karaoke = false;
2156 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2158 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2160 p_filter->p_sys->p_xml = p_xml_reader;
2163 /* Look for Root Node */
2164 if( xml_ReaderRead( p_xml_reader ) == 1 )
2166 char *psz_node = xml_ReaderName( p_xml_reader );
2168 if( !strcasecmp( "karaoke", psz_node ) )
2170 /* We're going to have to render the text a number
2171 * of times to show the progress marker on the text.
2173 var_SetBool( p_filter, "text-rerender", true );
2176 else if( !strcasecmp( "text", psz_node ) )
2182 /* Only text and karaoke tags are supported */
2183 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2184 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2185 p_xml_reader = NULL;
2197 uint32_t i_runs = 0;
2198 uint32_t i_k_runs = 0;
2199 uint32_t *pi_run_lengths = NULL;
2200 uint32_t *pi_k_run_lengths = NULL;
2201 uint32_t *pi_k_durations = NULL;
2202 ft_style_t **pp_styles = NULL;
2204 line_desc_t *p_lines = NULL;
2206 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2207 sizeof( uint32_t ) );
2210 rv = ProcessNodes( p_filter, p_xml_reader,
2211 p_region_in->p_style, psz_text, &i_len,
2212 &i_runs, &pi_run_lengths, &pp_styles,
2213 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2216 p_region_out->i_x = p_region_in->i_x;
2217 p_region_out->i_y = p_region_in->i_y;
2219 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2221 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2222 pi_run_lengths, pp_styles, &p_lines,
2223 &result, b_karaoke, i_k_runs,
2224 pi_k_run_lengths, pi_k_durations );
2227 for( uint_fast32_t k=0; k<i_runs; k++)
2228 DeleteStyle( pp_styles[k] );
2230 free( pi_run_lengths );
2233 /* Don't attempt to render text that couldn't be layed out
2235 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2237 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2238 Render( p_filter, p_region_out, p_lines,
2239 result.x, result.y );
2241 RenderYUVA( p_filter, p_region_out, p_lines,
2242 result.x, result.y );
2245 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2246 FreeLines( p_lines );
2248 stream_Delete( p_sub );
2252 static char* FontConfig_Select( FcConfig* priv, const char* family,
2253 bool b_bold, bool b_italic, int *i_idx )
2256 FcPattern *pat, *p_pat;
2260 pat = FcPatternCreate();
2261 if (!pat) return NULL;
2263 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2264 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2265 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2266 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2268 FcDefaultSubstitute( pat );
2270 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2272 FcPatternDestroy( pat );
2276 p_pat = FcFontMatch( priv, pat, &result );
2277 FcPatternDestroy( pat );
2278 if( !p_pat ) return NULL;
2280 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2281 || ( val_b != FcTrue ) )
2283 FcPatternDestroy( p_pat );
2286 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2291 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2293 FcPatternDestroy( p_pat );
2298 if( strcasecmp((const char*)val_s, family ) != 0 )
2299 msg_Warn( p_filter, "fontconfig: selected font family is not"
2300 "the requested one: '%s' != '%s'\n",
2301 (const char*)val_s, family );
2304 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2306 FcPatternDestroy( p_pat );
2310 FcPatternDestroy( p_pat );
2311 return strdup( (const char*)val_s );
2315 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2316 uint32_t **psz_text_out, uint32_t *pi_runs,
2317 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2318 ft_style_t *p_style )
2320 VLC_UNUSED(p_filter);
2321 VLC_UNUSED(psz_text_in);
2322 VLC_UNUSED(psz_text_out);
2323 VLC_UNUSED(pi_runs);
2324 VLC_UNUSED(ppi_run_lengths);
2325 VLC_UNUSED(ppp_styles);
2326 VLC_UNUSED(p_style);
2329 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2330 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2331 bool b_uline, bool b_through )
2334 VLC_UNUSED(p_fonts);
2336 VLC_UNUSED(b_italic);
2337 VLC_UNUSED(b_uline);
2338 VLC_UNUSED(b_through);
2343 static void FreeLine( line_desc_t *p_line )
2346 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2348 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2350 free( p_line->pp_glyphs );
2351 free( p_line->p_glyph_pos );
2352 free( p_line->p_fg_rgb );
2353 free( p_line->p_bg_rgb );
2354 free( p_line->p_fg_bg_ratio );
2355 free( p_line->pi_underline_offset );
2356 free( p_line->pi_underline_thickness );
2360 static void FreeLines( line_desc_t *p_lines )
2362 line_desc_t *p_line, *p_next;
2364 if( !p_lines ) return;
2366 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2368 p_next = p_line->p_next;
2373 static line_desc_t *NewLine( int i_count )
2375 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2377 if( !p_line ) return NULL;
2378 p_line->i_height = 0;
2379 p_line->i_width = 0;
2380 p_line->p_next = NULL;
2382 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2383 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2384 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2385 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2386 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2387 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2388 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2389 if( ( p_line->pp_glyphs == NULL ) ||
2390 ( p_line->p_glyph_pos == NULL ) ||
2391 ( p_line->p_fg_rgb == NULL ) ||
2392 ( p_line->p_bg_rgb == NULL ) ||
2393 ( p_line->p_fg_bg_ratio == NULL ) ||
2394 ( p_line->pi_underline_offset == NULL ) ||
2395 ( p_line->pi_underline_thickness == NULL ) )
2397 free( p_line->pi_underline_thickness );
2398 free( p_line->pi_underline_offset );
2399 free( p_line->p_fg_rgb );
2400 free( p_line->p_bg_rgb );
2401 free( p_line->p_fg_bg_ratio );
2402 free( p_line->p_glyph_pos );
2403 free( p_line->pp_glyphs );
2407 p_line->pp_glyphs[0] = NULL;
2408 p_line->b_new_color_mode = false;
2413 static int GetFontSize( filter_t *p_filter )
2415 filter_sys_t *p_sys = p_filter->p_sys;
2419 if( p_sys->i_default_font_size )
2421 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2422 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2424 i_size = p_sys->i_default_font_size;
2428 var_Get( p_filter, "freetype-rel-fontsize", &val );
2431 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2432 p_filter->p_sys->i_display_height =
2433 p_filter->fmt_out.video.i_height;
2438 msg_Warn( p_filter, "invalid fontsize, using 12" );
2439 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2440 i_size = 12 * val.i_int / 1000;
2447 static int SetFontSize( filter_t *p_filter, int i_size )
2449 filter_sys_t *p_sys = p_filter->p_sys;
2453 i_size = GetFontSize( p_filter );
2455 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2458 p_sys->i_font_size = i_size;
2460 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2462 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2463 return VLC_EGENERIC;
2469 static void YUVFromRGB( uint32_t i_argb,
2470 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2472 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2473 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2474 int i_blue = ( i_argb & 0x000000ff );
2476 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2477 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2478 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2479 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2480 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2481 -585 * i_blue + 4096 + 1048576) >> 13, 240);