1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
42 #include <vlc_dialog.h>
43 #include <vlc_memory.h>
48 #include <freetype/ftsynth.h>
49 #include FT_FREETYPE_H
51 #define FT_FLOOR(X) ((X & -64) >> 6)
52 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
54 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
59 #define FC_DEFAULT_FONT "Arial Black"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
66 #elif defined( HAVE_MAEMO )
67 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
68 #define FC_DEFAULT_FONT "Nokia Sans Bold"
70 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
71 #define FC_DEFAULT_FONT "Serif Bold"
74 #if defined(HAVE_FRIBIDI)
75 #include <fribidi/fribidi.h>
78 #ifdef HAVE_FONTCONFIG
79 #include <fontconfig/fontconfig.h>
81 #define DEFAULT_FONT FC_DEFAULT_FONT
86 /*****************************************************************************
88 *****************************************************************************/
89 static int Create ( vlc_object_t * );
90 static void Destroy( vlc_object_t * );
92 #define FONT_TEXT N_("Font")
94 #ifdef HAVE_FONTCONFIG
95 #define FONT_LONGTEXT N_("Font family for the font you want to use")
97 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
100 #define FONTSIZE_TEXT N_("Font size in pixels")
101 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
102 "that will be rendered on the video. " \
103 "If set to something different than 0 this option will override the " \
104 "relative font size." )
105 #define OPACITY_TEXT N_("Opacity")
106 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
107 "text that will be rendered on the video. 0 = transparent, " \
108 "255 = totally opaque. " )
109 #define COLOR_TEXT N_("Text default color")
110 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
111 "the video. This must be an hexadecimal (like HTML colors). The first two "\
112 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
113 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
114 #define FONTSIZER_TEXT N_("Relative font size")
115 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
116 "fonts that will be rendered on the video. If absolute font size is set, "\
117 "relative size will be overriden." )
119 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
120 static const char *const ppsz_sizes_text[] = {
121 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
122 #define YUVP_TEXT N_("Use YUVP renderer")
123 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
124 "This option is only needed if you want to encode into DVB subtitles" )
125 #define EFFECT_TEXT N_("Font Effect")
126 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
127 "text to improve its readability." )
129 #define EFFECT_BACKGROUND 1
130 #define EFFECT_OUTLINE 2
131 #define EFFECT_OUTLINE_FAT 3
133 static int const pi_effects[] = { 1, 2, 3 };
134 static const char *const ppsz_effects_text[] = {
135 N_("Background"),N_("Outline"), N_("Fat Outline") };
136 static const int pi_color_values[] = {
137 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
138 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
139 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
141 static const char *const ppsz_color_descriptions[] = {
142 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
143 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
144 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
147 set_shortname( N_("Text renderer"))
148 set_description( N_("Freetype2 font renderer") )
149 set_category( CAT_VIDEO )
150 set_subcategory( SUBCAT_VIDEO_SUBPIC )
152 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
155 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
156 FONTSIZE_LONGTEXT, true )
158 /* opacity valid on 0..255, with default 255 = fully opaque */
159 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
160 OPACITY_TEXT, OPACITY_LONGTEXT, true )
162 /* hook to the color values list, with default 0x00ffffff = white */
163 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
164 COLOR_LONGTEXT, false )
165 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
167 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
168 FONTSIZER_LONGTEXT, false )
169 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
170 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
171 EFFECT_LONGTEXT, false )
172 change_integer_list( pi_effects, ppsz_effects_text, NULL )
174 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
175 YUVP_LONGTEXT, true )
176 set_capability( "text renderer", 100 )
177 add_shortcut( "text" )
178 set_callbacks( Create, Destroy )
183 /*****************************************************************************
185 *****************************************************************************/
187 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
188 static int RenderText( filter_t *, subpicture_region_t *,
189 subpicture_region_t * );
190 #ifdef HAVE_FONTCONFIG
191 static int RenderHtml( filter_t *, subpicture_region_t *,
192 subpicture_region_t * );
193 static char *FontConfig_Select( FcConfig *, const char *,
198 static int LoadFontsFromAttachments( filter_t *p_filter );
200 static int GetFontSize( filter_t *p_filter );
201 static int SetFontSize( filter_t *, int );
202 static void YUVFromRGB( uint32_t i_argb,
203 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
205 typedef struct line_desc_t line_desc_t;
208 /** NULL-terminated list of glyphs making the string */
209 FT_BitmapGlyph *pp_glyphs;
210 /** list of relative positions for the glyphs */
211 FT_Vector *p_glyph_pos;
212 /** list of RGB information for styled text
213 * -- if the rendering mode supports it (RenderYUVA) and
214 * b_new_color_mode is set, then it becomes possible to
215 * have multicoloured text within the subtitles. */
218 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
219 bool b_new_color_mode;
220 /** underline information -- only supplied if text should be underlined */
221 int *pi_underline_offset;
222 uint16_t *pi_underline_thickness;
226 int i_red, i_green, i_blue;
231 static line_desc_t *NewLine( int );
236 uint32_t i_font_color; /* ARGB */
237 uint32_t i_karaoke_bg_color; /* ARGB */
245 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
246 static void FreeLines( line_desc_t * );
247 static void FreeLine( line_desc_t * );
249 /*****************************************************************************
250 * filter_sys_t: freetype local data
251 *****************************************************************************
252 * This structure is part of the video output thread descriptor.
253 * It describes the freetype specific properties of an output thread.
254 *****************************************************************************/
257 FT_Library p_library; /* handle to library */
258 FT_Face p_face; /* handle to face object */
260 uint8_t i_font_opacity;
265 int i_default_font_size;
266 int i_display_height;
267 #ifdef HAVE_FONTCONFIG
268 char* psz_fontfamily;
272 input_attachment_t **pp_font_attachments;
273 int i_font_attachments;
277 #define UCHAR uint32_t
278 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
279 #define TR_FONT_STYLE_PTR ft_style_t *
281 #include "text_renderer.h"
283 /*****************************************************************************
284 * Create: allocates osd-text video thread output method
285 *****************************************************************************
286 * This function allocates and initializes a Clone vout method.
287 *****************************************************************************/
288 static int Create( vlc_object_t *p_this )
290 filter_t *p_filter = (filter_t *)p_this;
292 char *psz_fontfile=NULL;
293 char *psz_fontfamily=NULL;
294 int i_error,fontindex;
296 #ifdef HAVE_FONTCONFIG
297 FcPattern *fontpattern = NULL, *fontmatch = NULL;
298 /* Initialise result to Match, as fontconfig doesnt
299 * really set this other than some error-cases */
300 FcResult fontresult = FcResultMatch;
304 /* Allocate structure */
305 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
308 #ifdef HAVE_FONTCONFIG
309 p_sys->psz_fontfamily = NULL;
313 p_sys->p_library = 0;
314 p_sys->i_font_size = 0;
315 p_sys->i_display_height = 0;
317 var_Create( p_filter, "freetype-rel-fontsize",
318 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
320 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
321 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
322 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
323 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
324 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
325 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
326 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
329 if( !psz_fontfamily || !*psz_fontfamily )
331 free( psz_fontfamily );
332 #ifdef HAVE_FONTCONFIG
333 psz_fontfamily=strdup( DEFAULT_FONT );
335 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
336 if( !psz_fontfamily )
339 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
340 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
342 strcpy( psz_fontfamily, DEFAULT_FONT );
344 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
348 #ifdef HAVE_FONTCONFIG
349 /* Lets find some fontfile from freetype-font variable family */
351 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
355 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
356 _("Building font cache"),
357 _("Please wait while your font cache is rebuilt.\n"
358 "This should take less than few minutes."), NULL );
359 char *path = xmalloc( PATH_MAX + 1 );
360 /* Fontconfig doesnt seem to know where windows fonts are with
361 * current contribs. So just tell default windows font directory
362 * is the place to search fonts
364 GetWindowsDirectory( path, PATH_MAX + 1 );
365 strcat( path, "\\fonts" );
367 dialog_ProgressSet( p_dialog, NULL, 0.4 );
369 FcConfigAppFontAddDir( NULL , path );
374 dialog_ProgressSet( p_dialog, NULL, 0.5 );
378 msg_Dbg( p_filter, "Building font database.");
380 FcConfigBuildFonts( NULL );
383 msg_Dbg( p_filter, "Finished building font database." );
384 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
386 fontpattern = FcPatternCreate();
390 msg_Err( p_filter, "Creating fontpattern failed");
396 dialog_ProgressSet( p_dialog, NULL, 0.7 );
398 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
399 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
400 free( psz_fontsize );
402 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
404 msg_Err( p_filter, "FontSubstitute failed");
407 FcDefaultSubstitute( fontpattern );
411 dialog_ProgressSet( p_dialog, NULL, 0.8 );
413 /* testing fontresult here doesn't do any good really, but maybe it will
414 * in future as fontconfig code doesn't set it in all cases and just
415 * returns NULL or doesn't set to to Match on all Match cases.*/
416 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
417 if( !fontmatch || fontresult == FcResultNoMatch )
419 msg_Err( p_filter, "Fontmatching failed");
423 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
424 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
427 msg_Err( p_filter, "Failed to get fontfile");
431 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
432 psz_fontfile ? psz_fontfile : "(null)" );
433 p_sys->psz_fontfamily = strdup( psz_fontfamily );
437 dialog_ProgressSet( p_dialog, NULL, 1.0 );
438 dialog_ProgressDestroy( p_dialog );
444 #ifdef HAVE_FONTCONFIG
445 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
446 psz_fontfile = psz_fontfamily;
451 i_error = FT_Init_FreeType( &p_sys->p_library );
454 msg_Err( p_filter, "couldn't initialize freetype" );
458 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
459 fontindex, &p_sys->p_face );
461 if( i_error == FT_Err_Unknown_File_Format )
463 msg_Err( p_filter, "file %s have unknown format",
464 psz_fontfile ? psz_fontfile : "(null)" );
469 msg_Err( p_filter, "failed to load font file %s",
470 psz_fontfile ? psz_fontfile : "(null)" );
474 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
477 msg_Err( p_filter, "font has no unicode translation table" );
481 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
483 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
486 p_sys->pp_font_attachments = NULL;
487 p_sys->i_font_attachments = 0;
489 p_filter->pf_render_text = RenderText;
490 #ifdef HAVE_FONTCONFIG
491 p_filter->pf_render_html = RenderHtml;
492 FcPatternDestroy( fontmatch );
493 FcPatternDestroy( fontpattern );
495 p_filter->pf_render_html = NULL;
498 free( psz_fontfamily );
499 LoadFontsFromAttachments( p_filter );
504 #ifdef HAVE_FONTCONFIG
505 if( fontmatch ) FcPatternDestroy( fontmatch );
506 if( fontpattern ) FcPatternDestroy( fontpattern );
508 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
509 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
510 free( psz_fontfamily );
515 /*****************************************************************************
516 * Destroy: destroy Clone video thread output method
517 *****************************************************************************
518 * Clean up all data and library connections
519 *****************************************************************************/
520 static void Destroy( vlc_object_t *p_this )
522 filter_t *p_filter = (filter_t *)p_this;
523 filter_sys_t *p_sys = p_filter->p_sys;
525 if( p_sys->pp_font_attachments )
529 for( k = 0; k < p_sys->i_font_attachments; k++ )
530 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
532 free( p_sys->pp_font_attachments );
535 #ifdef HAVE_FONTCONFIG
536 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
537 free( p_sys->psz_fontfamily );
540 /* FcFini asserts calling the subfunction FcCacheFini()
541 * even if no other library functions have been made since FcInit(),
542 * so don't call it. */
544 FT_Done_Face( p_sys->p_face );
545 FT_Done_FreeType( p_sys->p_library );
549 /*****************************************************************************
550 * Make any TTF/OTF fonts present in the attachments of the media file
551 * and store them for later use by the FreeType Engine
552 *****************************************************************************/
553 static int LoadFontsFromAttachments( filter_t *p_filter )
555 filter_sys_t *p_sys = p_filter->p_sys;
556 input_thread_t *p_input;
557 input_attachment_t **pp_attachments;
558 int i_attachments_cnt;
560 int rv = VLC_SUCCESS;
562 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
566 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
568 vlc_object_release(p_input);
572 p_sys->i_font_attachments = 0;
573 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
574 if(! p_sys->pp_font_attachments )
577 for( k = 0; k < i_attachments_cnt; k++ )
579 input_attachment_t *p_attach = pp_attachments[k];
581 if( p_sys->pp_font_attachments )
583 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
584 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
585 ( p_attach->i_data > 0 ) &&
586 ( p_attach->p_data != NULL ) )
588 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
592 vlc_input_attachment_Delete( p_attach );
597 vlc_input_attachment_Delete( p_attach );
600 free( pp_attachments );
602 vlc_object_release(p_input);
607 /*****************************************************************************
608 * Render: place string in picture
609 *****************************************************************************
610 * This function merges the previously rendered freetype glyphs into a picture
611 *****************************************************************************/
612 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
613 line_desc_t *p_line, int i_width, int i_height )
615 VLC_UNUSED(p_filter);
616 static const uint8_t pi_gamma[16] =
617 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
618 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
622 int i, x, y, i_pitch;
623 uint8_t i_y; /* YUV values, derived from incoming RGB */
626 /* Create a new subpicture region */
627 memset( &fmt, 0, sizeof(video_format_t) );
628 fmt.i_chroma = VLC_CODEC_YUVP;
629 fmt.i_width = fmt.i_visible_width = i_width + 4;
630 fmt.i_height = fmt.i_visible_height = i_height + 4;
631 if( p_region->fmt.i_visible_width > 0 )
632 fmt.i_visible_width = p_region->fmt.i_visible_width;
633 if( p_region->fmt.i_visible_height > 0 )
634 fmt.i_visible_height = p_region->fmt.i_visible_height;
635 fmt.i_x_offset = fmt.i_y_offset = 0;
637 assert( !p_region->p_picture );
638 p_region->p_picture = picture_NewFromFormat( &fmt );
639 if( !p_region->p_picture )
643 /* Calculate text color components */
644 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
645 25 * p_line->i_blue + 128) >> 8) + 16;
646 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
647 112 * p_line->i_blue + 128) >> 8) + 128;
648 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
649 18 * p_line->i_blue + 128) >> 8) + 128;
652 fmt.p_palette->i_entries = 16;
653 for( i = 0; i < 8; i++ )
655 fmt.p_palette->palette[i][0] = 0;
656 fmt.p_palette->palette[i][1] = 0x80;
657 fmt.p_palette->palette[i][2] = 0x80;
658 fmt.p_palette->palette[i][3] = pi_gamma[i];
659 fmt.p_palette->palette[i][3] =
660 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
662 for( i = 8; i < fmt.p_palette->i_entries; i++ )
664 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
665 fmt.p_palette->palette[i][1] = i_u;
666 fmt.p_palette->palette[i][2] = i_v;
667 fmt.p_palette->palette[i][3] = pi_gamma[i];
668 fmt.p_palette->palette[i][3] =
669 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
672 p_dst = p_region->p_picture->Y_PIXELS;
673 i_pitch = p_region->p_picture->Y_PITCH;
675 /* Initialize the region pixels */
676 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
678 for( ; p_line != NULL; p_line = p_line->p_next )
680 int i_glyph_tmax = 0;
681 int i_bitmap_offset, i_offset, i_align_offset = 0;
682 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
684 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
685 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
688 if( p_line->i_width < i_width )
690 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
692 i_align_offset = i_width - p_line->i_width;
694 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
696 i_align_offset = ( i_width - p_line->i_width ) / 2;
700 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
702 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
704 i_offset = ( p_line->p_glyph_pos[ i ].y +
705 i_glyph_tmax - p_glyph->top + 2 ) *
706 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
709 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
711 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
713 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
715 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
722 /* Outlining (find something better than nearest neighbour filtering ?) */
725 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
726 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
727 uint8_t left, current;
729 for( y = 1; y < (int)fmt.i_height - 1; y++ )
731 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
732 p_dst += p_region->p_picture->Y_PITCH;
735 for( x = 1; x < (int)fmt.i_width - 1; x++ )
738 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
739 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;
743 memset( p_top, 0, fmt.i_width );
749 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
750 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
751 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
752 int i_glyph_tmax, int i_align_offset,
753 uint8_t i_y, uint8_t i_u, uint8_t i_v,
754 subpicture_region_t *p_region)
758 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
760 p_dst_y = p_region->p_picture->Y_PIXELS;
761 p_dst_u = p_region->p_picture->U_PIXELS;
762 p_dst_v = p_region->p_picture->V_PIXELS;
763 p_dst_a = p_region->p_picture->A_PIXELS;
764 i_pitch = p_region->p_picture->A_PITCH;
766 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
767 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
769 for( y = 0; y < i_line_thickness; y++ )
771 int i_extra = p_this_glyph->bitmap.width;
775 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
776 (p_this_glyph_pos->x + p_this_glyph->left);
778 for( x = 0; x < i_extra; x++ )
782 /* break the underline around the tails of any glyphs which cross it */
783 /* Strikethrough doesn't get broken */
784 for( z = x - i_line_thickness;
785 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
788 if( p_next_glyph && ( z >= i_extra ) )
790 int i_row = i_line_offset + p_next_glyph->top + y;
792 if( ( p_next_glyph->bitmap.rows > i_row ) &&
793 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
798 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
800 int i_row = i_line_offset + p_this_glyph->top + y;
802 if( ( p_this_glyph->bitmap.rows > i_row ) &&
803 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
812 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
813 p_dst_u[i_offset+x] = i_u;
814 p_dst_v[i_offset+x] = i_v;
815 p_dst_a[i_offset+x] = 255;
822 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
824 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
825 int i_pitch = p_region->p_picture->A_PITCH;
828 for( ; p_line != NULL; p_line = p_line->p_next )
830 int i_glyph_tmax=0, i = 0;
831 int i_bitmap_offset, i_offset, i_align_offset = 0;
832 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
834 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
835 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
838 if( p_line->i_width < i_width )
840 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
842 i_align_offset = i_width - p_line->i_width;
844 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
846 i_align_offset = ( i_width - p_line->i_width ) / 2;
850 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
852 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
854 i_offset = ( p_line->p_glyph_pos[ i ].y +
855 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
856 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
857 i_align_offset +xoffset;
859 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
861 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
863 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
864 if( p_dst[i_offset+x] <
865 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
867 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
876 /*****************************************************************************
877 * Render: place string in picture
878 *****************************************************************************
879 * This function merges the previously rendered freetype glyphs into a picture
880 *****************************************************************************/
881 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
882 line_desc_t *p_line, int i_width, int i_height )
884 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
886 int i, x, y, i_pitch, i_alpha;
887 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
889 if( i_width == 0 || i_height == 0 )
892 /* Create a new subpicture region */
893 memset( &fmt, 0, sizeof(video_format_t) );
894 fmt.i_chroma = VLC_CODEC_YUVA;
895 fmt.i_width = fmt.i_visible_width = i_width + 6;
896 fmt.i_height = fmt.i_visible_height = i_height + 6;
897 if( p_region->fmt.i_visible_width > 0 )
898 fmt.i_visible_width = p_region->fmt.i_visible_width;
899 if( p_region->fmt.i_visible_height > 0 )
900 fmt.i_visible_height = p_region->fmt.i_visible_height;
901 fmt.i_x_offset = fmt.i_y_offset = 0;
903 p_region->p_picture = picture_NewFromFormat( &fmt );
904 if( !p_region->p_picture )
908 /* Calculate text color components */
909 YUVFromRGB( (p_line->i_red << 16) |
910 (p_line->i_green << 8) |
913 i_alpha = p_line->i_alpha;
915 p_dst_y = p_region->p_picture->Y_PIXELS;
916 p_dst_u = p_region->p_picture->U_PIXELS;
917 p_dst_v = p_region->p_picture->V_PIXELS;
918 p_dst_a = p_region->p_picture->A_PIXELS;
919 i_pitch = p_region->p_picture->A_PITCH;
921 /* Initialize the region pixels */
922 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
924 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
925 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
926 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
927 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
931 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
932 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
933 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
934 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
936 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
937 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
939 DrawBlack( p_line, i_width, p_region, 0, 0);
940 DrawBlack( p_line, i_width, p_region, -1, 0);
941 DrawBlack( p_line, i_width, p_region, 0, -1);
942 DrawBlack( p_line, i_width, p_region, 1, 0);
943 DrawBlack( p_line, i_width, p_region, 0, 1);
946 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
948 DrawBlack( p_line, i_width, p_region, -1, -1);
949 DrawBlack( p_line, i_width, p_region, -1, 1);
950 DrawBlack( p_line, i_width, p_region, 1, -1);
951 DrawBlack( p_line, i_width, p_region, 1, 1);
953 DrawBlack( p_line, i_width, p_region, -2, 0);
954 DrawBlack( p_line, i_width, p_region, 0, -2);
955 DrawBlack( p_line, i_width, p_region, 2, 0);
956 DrawBlack( p_line, i_width, p_region, 0, 2);
958 DrawBlack( p_line, i_width, p_region, -2, -2);
959 DrawBlack( p_line, i_width, p_region, -2, 2);
960 DrawBlack( p_line, i_width, p_region, 2, -2);
961 DrawBlack( p_line, i_width, p_region, 2, 2);
963 DrawBlack( p_line, i_width, p_region, -3, 0);
964 DrawBlack( p_line, i_width, p_region, 0, -3);
965 DrawBlack( p_line, i_width, p_region, 3, 0);
966 DrawBlack( p_line, i_width, p_region, 0, 3);
969 for( ; p_line != NULL; p_line = p_line->p_next )
971 int i_glyph_tmax = 0;
972 int i_bitmap_offset, i_offset, i_align_offset = 0;
973 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
975 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
976 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
979 if( p_line->i_width < i_width )
981 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
983 i_align_offset = i_width - p_line->i_width;
985 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
987 i_align_offset = ( i_width - p_line->i_width ) / 2;
991 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
993 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
995 i_offset = ( p_line->p_glyph_pos[ i ].y +
996 i_glyph_tmax - p_glyph->top + 3 ) *
997 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1000 if( p_line->b_new_color_mode )
1002 /* Every glyph can (and in fact must) have its own color */
1003 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1006 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1008 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1010 uint8_t i_y_local = i_y;
1011 uint8_t i_u_local = i_u;
1012 uint8_t i_v_local = i_v;
1014 if( p_line->p_fg_bg_ratio != 0x00 )
1016 int i_split = p_glyph->bitmap.width *
1017 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1021 YUVFromRGB( p_line->p_bg_rgb[ i ],
1022 &i_y_local, &i_u_local, &i_v_local );
1026 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1028 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1029 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1031 p_dst_u[i_offset+x] = i_u;
1032 p_dst_v[i_offset+x] = i_v;
1034 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1035 p_dst_a[i_offset+x] = 0xff;
1038 i_offset += i_pitch;
1041 if( p_line->pi_underline_thickness[ i ] )
1043 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1044 p_line->pi_underline_offset[ i ],
1045 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1046 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1047 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1048 i_glyph_tmax, i_align_offset,
1055 /* Apply the alpha setting */
1056 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1057 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1063 * This function renders a text subpicture region into another one.
1064 * It also calculates the size needed for this string, and renders the
1065 * needed glyphs into memory. It is used as pf_add_string callback in
1066 * the vout method by this module
1068 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1069 subpicture_region_t *p_region_in )
1071 filter_sys_t *p_sys = p_filter->p_sys;
1072 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1073 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1074 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1075 int i_string_length;
1077 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1078 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1088 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1089 psz_string = p_region_in->psz_text;
1090 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1092 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1093 i_scale = val.i_int;
1095 if( p_region_in->p_style )
1097 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1098 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1099 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1103 i_font_color = p_sys->i_font_color;
1104 i_font_alpha = 255 - p_sys->i_font_opacity;
1105 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1108 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1109 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1110 SetFontSize( p_filter, i_font_size );
1112 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1113 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1114 i_blue = i_font_color & 0x000000FF;
1116 result.x = result.y = 0;
1117 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1119 psz_unicode = psz_unicode_orig =
1120 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1121 if( psz_unicode == NULL )
1123 #if defined(WORDS_BIGENDIAN)
1124 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1126 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1128 if( iconv_handle == (vlc_iconv_t)-1 )
1130 msg_Warn( p_filter, "unable to do conversion" );
1136 const char *p_in_buffer = psz_string;
1137 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1138 i_in_bytes = strlen( psz_string );
1139 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1140 i_out_bytes_left = i_out_bytes;
1141 p_out_buffer = (char *)psz_unicode;
1142 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1144 &p_out_buffer, &i_out_bytes_left );
1146 vlc_iconv_close( iconv_handle );
1150 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1151 "bytes left %u", (unsigned)i_in_bytes );
1154 *(uint32_t*)p_out_buffer = 0;
1155 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1158 #if defined(HAVE_FRIBIDI)
1160 uint32_t *p_fribidi_string;
1161 int32_t start_pos, pos = 0;
1163 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1164 if( !p_fribidi_string )
1167 /* Do bidi conversion line-by-line */
1168 while( pos < i_string_length )
1170 while( pos < i_string_length )
1172 i_char = psz_unicode[pos];
1173 if (i_char != '\r' && i_char != '\n')
1175 p_fribidi_string[pos] = i_char;
1179 while( pos < i_string_length )
1181 i_char = psz_unicode[pos];
1182 if (i_char == '\r' || i_char == '\n')
1186 if (pos > start_pos)
1188 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1189 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1192 (FriBidiChar*)p_fribidi_string + start_pos,
1197 free( psz_unicode_orig );
1198 psz_unicode = psz_unicode_orig = p_fribidi_string;
1199 p_fribidi_string[ i_string_length ] = 0;
1203 /* Calculate relative glyph positions and a bounding box for the
1205 if( !(p_line = NewLine( strlen( psz_string ))) )
1208 i_pen_x = i_pen_y = 0;
1210 psz_line_start = psz_unicode;
1212 #define face p_sys->p_face
1213 #define glyph face->glyph
1215 while( *psz_unicode )
1217 i_char = *psz_unicode++;
1218 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1223 if( i_char == '\n' )
1225 psz_line_start = psz_unicode;
1226 if( !(p_next = NewLine( strlen( psz_string ))) )
1228 p_line->p_next = p_next;
1229 p_line->i_width = line.xMax;
1230 p_line->i_height = face->size->metrics.height >> 6;
1231 p_line->pp_glyphs[ i ] = NULL;
1232 p_line->i_alpha = i_font_alpha;
1233 p_line->i_red = i_red;
1234 p_line->i_green = i_green;
1235 p_line->i_blue = i_blue;
1238 result.x = __MAX( result.x, line.xMax );
1239 result.y += face->size->metrics.height >> 6;
1242 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1243 i_pen_y += face->size->metrics.height >> 6;
1245 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1250 i_glyph_index = FT_Get_Char_Index( face, i_char );
1251 if( p_sys->i_use_kerning && i_glyph_index
1255 FT_Get_Kerning( face, i_previous, i_glyph_index,
1256 ft_kerning_default, &delta );
1257 i_pen_x += delta.x >> 6;
1260 p_line->p_glyph_pos[ i ].x = i_pen_x;
1261 p_line->p_glyph_pos[ i ].y = i_pen_y;
1262 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1265 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1269 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1272 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1276 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1277 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1280 FT_Done_Glyph( tmp_glyph );
1283 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1286 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1287 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1288 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1290 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1291 p_line->pp_glyphs[ i ] = NULL;
1293 p_line = NewLine( strlen( psz_string ));
1294 if( p_prev ) p_prev->p_next = p_line;
1295 else p_lines = p_line;
1297 uint32_t *psz_unicode_saved = psz_unicode;
1298 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1302 if( psz_unicode == psz_line_start )
1303 { /* try harder to break that line */
1304 psz_unicode = psz_unicode_saved;
1305 while( psz_unicode > psz_line_start &&
1306 *psz_unicode != '_' && *psz_unicode != '/' &&
1307 *psz_unicode != '\\' && *psz_unicode != '.' )
1312 if( psz_unicode == psz_line_start )
1314 msg_Warn( p_filter, "unbreakable string" );
1319 *psz_unicode = '\n';
1321 psz_unicode = psz_line_start;
1324 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1327 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1328 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1330 i_previous = i_glyph_index;
1331 i_pen_x += glyph->advance.x >> 6;
1335 p_line->i_width = line.xMax;
1336 p_line->i_height = face->size->metrics.height >> 6;
1337 p_line->pp_glyphs[ i ] = NULL;
1338 p_line->i_alpha = i_font_alpha;
1339 p_line->i_red = i_red;
1340 p_line->i_green = i_green;
1341 p_line->i_blue = i_blue;
1342 result.x = __MAX( result.x, line.xMax );
1343 result.y += line.yMax - line.yMin;
1348 p_region_out->i_x = p_region_in->i_x;
1349 p_region_out->i_y = p_region_in->i_y;
1351 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1352 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1354 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1356 free( psz_unicode_orig );
1357 FreeLines( p_lines );
1361 free( psz_unicode_orig );
1362 FreeLines( p_lines );
1363 return VLC_EGENERIC;
1366 #ifdef HAVE_FONTCONFIG
1367 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1368 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1369 bool b_italic, bool b_uline, bool b_through )
1371 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1375 p_style->i_font_size = i_font_size;
1376 p_style->i_font_color = i_font_color;
1377 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1378 p_style->b_italic = b_italic;
1379 p_style->b_bold = b_bold;
1380 p_style->b_underline = b_uline;
1381 p_style->b_through = b_through;
1383 p_style->psz_fontname = strdup( psz_fontname );
1388 static void DeleteStyle( ft_style_t *p_style )
1392 free( p_style->psz_fontname );
1397 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1404 if(( s1->i_font_size == s2->i_font_size ) &&
1405 ( s1->i_font_color == s2->i_font_color ) &&
1406 ( s1->b_italic == s2->b_italic ) &&
1407 ( s1->b_through == s2->b_through ) &&
1408 ( s1->b_bold == s2->b_bold ) &&
1409 ( s1->b_underline == s2->b_underline ) &&
1410 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1417 static void IconvText( filter_t *p_filter, const char *psz_string,
1418 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1420 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1422 /* If memory hasn't been allocated for our output string, allocate it here
1423 * - the calling function must now be responsible for freeing it.
1425 if( !*ppsz_unicode )
1426 *ppsz_unicode = (uint32_t *)
1427 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1429 /* We don't need to handle a NULL pointer in *ppsz_unicode
1430 * if we are instead testing for a non NULL value like we are here */
1434 #if defined(WORDS_BIGENDIAN)
1435 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1437 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1439 if( iconv_handle != (vlc_iconv_t)-1 )
1441 char *p_in_buffer, *p_out_buffer;
1442 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1443 i_in_bytes = strlen( psz_string );
1444 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1445 i_out_bytes_left = i_out_bytes;
1446 p_in_buffer = (char *) psz_string;
1447 p_out_buffer = (char *) *ppsz_unicode;
1448 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1449 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1451 vlc_iconv_close( iconv_handle );
1455 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1456 "bytes left %u", (unsigned)i_in_bytes );
1460 *(uint32_t*)p_out_buffer = 0;
1462 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1467 msg_Warn( p_filter, "unable to do conversion" );
1472 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1473 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1474 bool b_uline, bool b_through )
1476 ft_style_t *p_style = NULL;
1478 char *psz_fontname = NULL;
1479 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1480 uint32_t i_karaoke_bg_color = i_font_color;
1481 int i_font_size = p_sys->i_font_size;
1483 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1484 &i_font_color, &i_karaoke_bg_color ))
1486 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1487 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1492 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1493 bool b_uline, bool b_through, bool b_bold,
1494 bool b_italic, int i_karaoke_bgcolor,
1495 line_desc_t *p_line, uint32_t *psz_unicode,
1496 int *pi_pen_x, int i_pen_y, int *pi_start,
1497 FT_Vector *p_result )
1502 bool b_first_on_line = true;
1505 int i_pen_x_start = *pi_pen_x;
1507 uint32_t *psz_unicode_start = psz_unicode;
1509 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1511 /* Account for part of line already in position */
1512 for( i=0; i<*pi_start; i++ )
1516 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1517 ft_glyph_bbox_pixels, &glyph_size );
1519 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1520 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1521 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1522 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1528 b_first_on_line = false;
1530 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1536 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1537 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1541 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1542 ft_kerning_default, &delta );
1543 *pi_pen_x += delta.x >> 6;
1545 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1546 p_line->p_glyph_pos[ i ].y = i_pen_y;
1548 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1552 "unable to render text FT_Load_Glyph returned %d", i_error );
1553 p_line->pp_glyphs[ i ] = NULL;
1554 return VLC_EGENERIC;
1557 /* Do synthetic styling now that Freetype supports it;
1558 * ie. if the font we have loaded is NOT already in the
1559 * style that the tags want, then switch it on; if they
1560 * are then don't. */
1561 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1562 FT_GlyphSlot_Embolden( p_face->glyph );
1563 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1564 FT_GlyphSlot_Oblique( p_face->glyph );
1566 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1570 "unable to render text FT_Get_Glyph returned %d", i_error );
1571 p_line->pp_glyphs[ i ] = NULL;
1572 return VLC_EGENERIC;
1574 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1575 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1578 FT_Done_Glyph( tmp_glyph );
1581 if( b_uline || b_through )
1583 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1584 p_face->size->metrics.y_scale));
1585 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1586 p_face->size->metrics.y_scale));
1588 p_line->pi_underline_offset[ i ] =
1589 ( aOffset < 0 ) ? -aOffset : aOffset;
1590 p_line->pi_underline_thickness[ i ] =
1591 ( aSize < 0 ) ? -aSize : aSize;
1594 /* Move the baseline to make it strikethrough instead of
1595 * underline. That means that strikethrough takes precedence
1597 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1598 p_face->size->metrics.y_scale));
1600 p_line->pi_underline_offset[ i ] -=
1601 ( aDescent < 0 ) ? -aDescent : aDescent;
1605 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1606 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1607 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1608 p_line->p_fg_bg_ratio[ i ] = 0x00;
1610 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1611 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1612 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1614 for( ; i >= *pi_start; i-- )
1615 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1618 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1622 if( psz_unicode == psz_unicode_start )
1624 if( b_first_on_line )
1626 msg_Warn( p_filter, "unbreakable string" );
1627 p_line->pp_glyphs[ i ] = NULL;
1628 return VLC_EGENERIC;
1630 *pi_pen_x = i_pen_x_start;
1632 p_line->i_width = line.xMax;
1633 p_line->i_height = __MAX( p_line->i_height,
1634 p_face->size->metrics.height >> 6 );
1635 p_line->pp_glyphs[ i ] = NULL;
1637 p_result->x = __MAX( p_result->x, line.xMax );
1638 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1639 i_yMax - i_yMin ) );
1644 *psz_unicode = '\n';
1646 psz_unicode = psz_unicode_start;
1647 *pi_pen_x = i_pen_x_start;
1655 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1656 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1658 i_previous = i_glyph_index;
1659 *pi_pen_x += p_face->glyph->advance.x >> 6;
1662 p_line->i_width = line.xMax;
1663 p_line->i_height = __MAX( p_line->i_height,
1664 p_face->size->metrics.height >> 6 );
1665 p_line->pp_glyphs[ i ] = NULL;
1667 p_result->x = __MAX( p_result->x, line.xMax );
1668 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1669 line.yMax - line.yMin ) );
1673 /* Get rid of any text processed - if necessary repositioning
1674 * at the start of a new line of text
1678 *psz_unicode_start = '\0';
1680 else if( psz_unicode > psz_unicode_start )
1682 for( i=0; psz_unicode[ i ]; i++ )
1683 psz_unicode_start[ i ] = psz_unicode[ i ];
1684 psz_unicode_start[ i ] = '\0';
1690 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1691 uint32_t **psz_text_out, uint32_t *pi_runs,
1692 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1693 ft_style_t *p_style )
1695 uint32_t i_string_length = 0;
1697 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1698 *psz_text_out += i_string_length;
1700 if( ppp_styles && ppi_run_lengths )
1704 /* XXX this logic looks somewhat broken */
1708 *ppp_styles = realloc_or_free( *ppp_styles,
1709 *pi_runs * sizeof( ft_style_t * ) );
1711 else if( *pi_runs == 1 )
1713 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1716 /* We have just malloc'ed this memory successfully -
1717 * *pi_runs HAS to be within the memory area of *ppp_styles */
1720 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1724 /* XXX more iffy logic */
1726 if( *ppi_run_lengths )
1728 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1729 *pi_runs * sizeof( uint32_t ) );
1731 else if( *pi_runs == 1 )
1733 *ppi_run_lengths = (uint32_t *)
1734 malloc( *pi_runs * sizeof( uint32_t ) );
1737 /* same remarks here */
1738 if( *ppi_run_lengths )
1740 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1743 /* If we couldn't use the p_style argument due to memory allocation
1744 * problems above, release it here.
1746 if( p_style ) DeleteStyle( p_style );
1749 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1753 for( k=0; k < p_sys->i_font_attachments; k++ )
1755 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1757 FT_Face p_face = NULL;
1759 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1767 bool match = !strcasecmp( p_face->family_name,
1768 p_style->psz_fontname );
1770 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1771 match = match && p_style->b_bold;
1773 match = match && !p_style->b_bold;
1775 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1776 match = match && p_style->b_italic;
1778 match = match && !p_style->b_italic;
1786 FT_Done_Face( p_face );
1791 return VLC_EGENERIC;
1794 static int ProcessLines( filter_t *p_filter,
1799 uint32_t *pi_run_lengths,
1800 ft_style_t **pp_styles,
1801 line_desc_t **pp_lines,
1803 FT_Vector *p_result,
1807 uint32_t *pi_k_run_lengths,
1808 uint32_t *pi_k_durations )
1810 filter_sys_t *p_sys = p_filter->p_sys;
1811 ft_style_t **pp_char_styles;
1812 int *p_new_positions = NULL;
1813 int8_t *p_levels = NULL;
1814 uint8_t *pi_karaoke_bar = NULL;
1818 /* Assign each character in the text string its style explicitly, so that
1819 * after the characters have been shuffled around by Fribidi, we can re-apply
1820 * the styles, and to simplify the calculation of runs within a line.
1822 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1823 if( !pp_char_styles )
1828 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1829 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1830 * we just won't be able to display the progress bar; at least we'll
1836 for( j = 0; j < i_runs; j++ )
1837 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1838 pp_char_styles[ i++ ] = pp_styles[ j ];
1840 #if defined(HAVE_FRIBIDI)
1842 ft_style_t **pp_char_styles_new;
1843 int *p_old_positions;
1844 uint32_t *p_fribidi_string;
1845 int start_pos, pos = 0;
1847 pp_char_styles_new = (ft_style_t **)
1848 malloc( i_len * sizeof( ft_style_t * ));
1850 p_fribidi_string = (uint32_t *)
1851 malloc( (i_len + 1) * sizeof(uint32_t) );
1852 p_old_positions = (int *)
1853 malloc( (i_len + 1) * sizeof( int ) );
1854 p_new_positions = (int *)
1855 malloc( (i_len + 1) * sizeof( int ) );
1856 p_levels = (int8_t *)
1857 malloc( (i_len + 1) * sizeof( int8_t ) );
1859 if( ! pp_char_styles_new ||
1860 ! p_fribidi_string ||
1861 ! p_old_positions ||
1862 ! p_new_positions ||
1866 free( p_old_positions );
1867 free( p_new_positions );
1868 free( p_fribidi_string );
1869 free( pp_char_styles_new );
1870 free( pi_karaoke_bar );
1872 free( pp_char_styles );
1876 /* Do bidi conversion line-by-line */
1879 while(pos < i_len) {
1880 if (psz_text[pos] != '\n')
1882 p_fribidi_string[pos] = psz_text[pos];
1883 pp_char_styles_new[pos] = pp_char_styles[pos];
1884 p_new_positions[pos] = pos;
1889 while(pos < i_len) {
1890 if (psz_text[pos] == '\n')
1894 if (pos > start_pos)
1896 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1897 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1898 pos - start_pos, &base_dir,
1899 (FriBidiChar*)p_fribidi_string + start_pos,
1900 p_new_positions + start_pos,
1902 p_levels + start_pos );
1903 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1905 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1906 p_old_positions[ j - start_pos ] ];
1907 p_new_positions[ j ] += start_pos;
1911 free( p_old_positions );
1912 free( pp_char_styles );
1913 pp_char_styles = pp_char_styles_new;
1914 psz_text = p_fribidi_string;
1915 p_fribidi_string[ i_len ] = 0;
1918 /* Work out the karaoke */
1919 if( pi_karaoke_bar )
1921 int64_t i_last_duration = 0;
1922 int64_t i_duration = 0;
1923 int64_t i_start_pos = 0;
1924 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1926 for( k = 0; k< i_k_runs; k++ )
1928 double fraction = 0.0;
1930 i_duration += pi_k_durations[ k ];
1932 if( i_duration < i_elapsed )
1934 /* Completely finished this run-length -
1935 * let it render normally */
1939 else if( i_elapsed < i_last_duration )
1941 /* Haven't got up to this segment yet -
1942 * render it completely in karaoke BG mode */
1948 /* Partway through this run */
1950 fraction = (double)(i_elapsed - i_last_duration) /
1951 (double)pi_k_durations[ k ];
1953 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1955 double shade = pi_k_run_lengths[ k ] * fraction;
1957 if( p_new_positions )
1958 j = p_new_positions[ i_start_pos + i ];
1960 j = i_start_pos + i;
1962 if( i < (uint32_t)shade )
1963 pi_karaoke_bar[ j ] = 0xff;
1964 else if( (double)i > shade )
1965 pi_karaoke_bar[ j ] = 0x00;
1968 shade -= (int)shade;
1969 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1970 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1974 i_last_duration = i_duration;
1975 i_start_pos += pi_k_run_lengths[ k ];
1979 free( p_new_positions );
1981 FT_Vector tmp_result;
1983 line_desc_t *p_line = NULL;
1984 line_desc_t *p_prev = NULL;
1990 p_result->x = p_result->y = 0;
1991 tmp_result.x = tmp_result.y = 0;
1994 for( k = 0; k <= (uint32_t) i_len; k++ )
1996 if( ( k == (uint32_t) i_len ) ||
1998 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2000 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2002 /* End of the current style run */
2003 FT_Face p_face = NULL;
2006 /* Look for a match amongst our attachments first */
2007 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2011 char *psz_fontfile = NULL;
2013 psz_fontfile = FontConfig_Select( NULL,
2014 p_style->psz_fontname,
2018 if( psz_fontfile && ! *psz_fontfile )
2020 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2021 " so using default font", p_style->psz_fontname,
2022 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2023 (p_style->b_bold ? "(Bold)" :
2024 (p_style->b_italic ? "(Italic)" : ""))) );
2025 free( psz_fontfile );
2026 psz_fontfile = NULL;
2031 if( FT_New_Face( p_sys->p_library,
2032 psz_fontfile, i_idx, &p_face ) )
2034 free( psz_fontfile );
2035 free( pp_char_styles );
2036 #if defined(HAVE_FRIBIDI)
2039 free( pi_karaoke_bar );
2040 return VLC_EGENERIC;
2042 free( psz_fontfile );
2046 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2048 /* We've loaded a font face which is unhelpful for actually
2049 * rendering text - fallback to the default one.
2051 FT_Done_Face( p_face );
2055 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2056 ft_encoding_unicode ) ||
2057 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2058 p_style->i_font_size ) )
2060 if( p_face ) FT_Done_Face( p_face );
2061 free( pp_char_styles );
2062 #if defined(HAVE_FRIBIDI)
2065 free( pi_karaoke_bar );
2066 return VLC_EGENERIC;
2068 p_sys->i_use_kerning =
2069 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2072 uint32_t *psz_unicode = (uint32_t *)
2073 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
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 memcpy( psz_unicode, psz_text + i_prev,
2086 sizeof( uint32_t ) * ( k - i_prev ) );
2087 psz_unicode[ k - i_prev ] = 0;
2088 while( *psz_unicode )
2092 if( !(p_line = NewLine( i_len - i_prev)) )
2094 if( p_face ) FT_Done_Face( p_face );
2095 free( pp_char_styles );
2096 free( psz_unicode );
2097 #if defined(HAVE_FRIBIDI)
2100 free( pi_karaoke_bar );
2103 /* New Color mode only works in YUVA rendering mode --
2104 * (RGB mode has palette constraints on it). We therefore
2105 * need to populate the legacy colour fields also.
2107 p_line->b_new_color_mode = true;
2108 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2109 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2110 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2111 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2112 p_line->p_next = NULL;
2114 i_pen_y += tmp_result.y;
2118 if( p_prev ) p_prev->p_next = p_line;
2119 else *pp_lines = p_line;
2122 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2123 p_style->i_font_color, p_style->b_underline,
2127 p_style->i_karaoke_bg_color,
2128 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2129 &tmp_result ) != VLC_SUCCESS )
2131 if( p_face ) FT_Done_Face( p_face );
2132 free( pp_char_styles );
2133 free( psz_unicode );
2134 #if defined(HAVE_FRIBIDI)
2137 free( pi_karaoke_bar );
2138 return VLC_EGENERIC;
2143 p_result->x = __MAX( p_result->x, tmp_result.x );
2144 p_result->y += tmp_result.y;
2149 if( *psz_unicode == '\n')
2153 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2155 *c_ptr = *(c_ptr+1);
2160 free( psz_unicode );
2161 if( p_face ) FT_Done_Face( p_face );
2165 free( pp_char_styles );
2166 #if defined(HAVE_FRIBIDI)
2171 p_result->x = __MAX( p_result->x, tmp_result.x );
2172 p_result->y += tmp_result.y;
2175 if( pi_karaoke_bar )
2178 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2180 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2182 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2186 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2188 /* 100% BG colour will render faster if we
2189 * instead make it 100% FG colour, so leave
2190 * the ratio alone and copy the value across
2192 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2196 if( pi_karaoke_bar[ i ] & 0x80 )
2198 /* Swap Left and Right sides over for Right aligned
2199 * language text (eg. Arabic, Hebrew)
2201 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2203 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2204 p_line->p_bg_rgb[ k ] = i_tmp;
2206 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2209 /* Jump over the '\n' at the line-end */
2212 free( pi_karaoke_bar );
2218 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2219 subpicture_region_t *p_region_in )
2221 int rv = VLC_SUCCESS;
2222 stream_t *p_sub = NULL;
2223 xml_reader_t *p_xml_reader = NULL;
2225 if( !p_region_in || !p_region_in->psz_html )
2226 return VLC_EGENERIC;
2228 /* Reset the default fontsize in case screen metrics have changed */
2229 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2231 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2232 (uint8_t *) p_region_in->psz_html,
2233 strlen( p_region_in->psz_html ),
2237 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2238 if( p_filter->p_sys->p_xml )
2240 bool b_karaoke = false;
2242 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2245 /* Look for Root Node */
2246 if( xml_ReaderRead( p_xml_reader ) == 1 )
2248 char *psz_node = xml_ReaderName( p_xml_reader );
2250 if( !strcasecmp( "karaoke", psz_node ) )
2252 /* We're going to have to render the text a number
2253 * of times to show the progress marker on the text.
2255 var_SetBool( p_filter, "text-rerender", true );
2258 else if( !strcasecmp( "text", psz_node ) )
2264 /* Only text and karaoke tags are supported */
2265 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2266 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2267 p_xml_reader = NULL;
2279 uint32_t i_runs = 0;
2280 uint32_t i_k_runs = 0;
2281 uint32_t *pi_run_lengths = NULL;
2282 uint32_t *pi_k_run_lengths = NULL;
2283 uint32_t *pi_k_durations = NULL;
2284 ft_style_t **pp_styles = NULL;
2286 line_desc_t *p_lines = NULL;
2288 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2289 sizeof( uint32_t ) );
2294 rv = ProcessNodes( p_filter, p_xml_reader,
2295 p_region_in->p_style, psz_text, &i_len,
2296 &i_runs, &pi_run_lengths, &pp_styles,
2298 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2301 p_region_out->i_x = p_region_in->i_x;
2302 p_region_out->i_y = p_region_in->i_y;
2304 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2306 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2307 pi_run_lengths, pp_styles, &p_lines, &result,
2308 b_karaoke, i_k_runs, pi_k_run_lengths,
2312 for( k=0; k<i_runs; k++)
2313 DeleteStyle( pp_styles[k] );
2315 free( pi_run_lengths );
2318 /* Don't attempt to render text that couldn't be layed out
2321 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2323 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2325 Render( p_filter, p_region_out, p_lines,
2326 result.x, result.y );
2330 RenderYUVA( p_filter, p_region_out, p_lines,
2331 result.x, result.y );
2335 FreeLines( p_lines );
2337 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2340 stream_Delete( p_sub );
2346 static char* FontConfig_Select( FcConfig* priv, const char* family,
2347 bool b_bold, bool b_italic, int *i_idx )
2350 FcPattern *pat, *p_pat;
2354 pat = FcPatternCreate();
2355 if (!pat) return NULL;
2357 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2358 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2359 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2360 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2362 FcDefaultSubstitute( pat );
2364 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2366 FcPatternDestroy( pat );
2370 p_pat = FcFontMatch( priv, pat, &result );
2371 FcPatternDestroy( pat );
2372 if( !p_pat ) return NULL;
2374 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2375 || ( val_b != FcTrue ) )
2377 FcPatternDestroy( p_pat );
2380 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2385 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2387 FcPatternDestroy( p_pat );
2392 if( strcasecmp((const char*)val_s, family ) != 0 )
2393 msg_Warn( p_filter, "fontconfig: selected font family is not"
2394 "the requested one: '%s' != '%s'\n",
2395 (const char*)val_s, family );
2398 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2400 FcPatternDestroy( p_pat );
2404 FcPatternDestroy( p_pat );
2405 return strdup( (const char*)val_s );
2409 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2410 uint32_t **psz_text_out, uint32_t *pi_runs,
2411 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2412 ft_style_t *p_style )
2414 VLC_UNUSED(p_filter);
2415 VLC_UNUSED(psz_text_in);
2416 VLC_UNUSED(psz_text_out);
2417 VLC_UNUSED(pi_runs);
2418 VLC_UNUSED(ppi_run_lengths);
2419 VLC_UNUSED(ppp_styles);
2420 VLC_UNUSED(p_style);
2423 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2424 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2425 bool b_uline, bool b_through )
2428 VLC_UNUSED(p_fonts);
2430 VLC_UNUSED(b_italic);
2431 VLC_UNUSED(b_uline);
2432 VLC_UNUSED(b_through);
2437 static void FreeLine( line_desc_t *p_line )
2440 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2442 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2444 free( p_line->pp_glyphs );
2445 free( p_line->p_glyph_pos );
2446 free( p_line->p_fg_rgb );
2447 free( p_line->p_bg_rgb );
2448 free( p_line->p_fg_bg_ratio );
2449 free( p_line->pi_underline_offset );
2450 free( p_line->pi_underline_thickness );
2454 static void FreeLines( line_desc_t *p_lines )
2456 line_desc_t *p_line, *p_next;
2458 if( !p_lines ) return;
2460 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2462 p_next = p_line->p_next;
2467 static line_desc_t *NewLine( int i_count )
2469 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2471 if( !p_line ) return NULL;
2472 p_line->i_height = 0;
2473 p_line->i_width = 0;
2474 p_line->p_next = NULL;
2476 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2477 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2478 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2479 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2480 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2481 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2482 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2483 if( ( p_line->pp_glyphs == NULL ) ||
2484 ( p_line->p_glyph_pos == NULL ) ||
2485 ( p_line->p_fg_rgb == NULL ) ||
2486 ( p_line->p_bg_rgb == NULL ) ||
2487 ( p_line->p_fg_bg_ratio == NULL ) ||
2488 ( p_line->pi_underline_offset == NULL ) ||
2489 ( p_line->pi_underline_thickness == NULL ) )
2491 free( p_line->pi_underline_thickness );
2492 free( p_line->pi_underline_offset );
2493 free( p_line->p_fg_rgb );
2494 free( p_line->p_bg_rgb );
2495 free( p_line->p_fg_bg_ratio );
2496 free( p_line->p_glyph_pos );
2497 free( p_line->pp_glyphs );
2501 p_line->pp_glyphs[0] = NULL;
2502 p_line->b_new_color_mode = false;
2507 static int GetFontSize( filter_t *p_filter )
2509 filter_sys_t *p_sys = p_filter->p_sys;
2513 if( p_sys->i_default_font_size )
2515 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2516 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2518 i_size = p_sys->i_default_font_size;
2522 var_Get( p_filter, "freetype-rel-fontsize", &val );
2525 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2526 p_filter->p_sys->i_display_height =
2527 p_filter->fmt_out.video.i_height;
2532 msg_Warn( p_filter, "invalid fontsize, using 12" );
2533 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2534 i_size = 12 * val.i_int / 1000;
2541 static int SetFontSize( filter_t *p_filter, int i_size )
2543 filter_sys_t *p_sys = p_filter->p_sys;
2547 i_size = GetFontSize( p_filter );
2549 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2552 p_sys->i_font_size = i_size;
2554 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2556 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2557 return VLC_EGENERIC;
2563 static void YUVFromRGB( uint32_t i_argb,
2564 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2566 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2567 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2568 int i_blue = ( i_argb & 0x000000ff );
2570 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2571 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2572 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2573 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2574 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2575 -585 * i_blue + 4096 + 1048576) >> 13, 240);