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 overridden." )
119 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
120 static const char *const ppsz_sizes_text[] = {
121 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
122 #define YUVP_TEXT N_("Use YUVP renderer")
123 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
124 "This option is only needed if you want to encode into DVB subtitles" )
125 #define EFFECT_TEXT N_("Font Effect")
126 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
127 "text to improve its readability." )
129 #define EFFECT_BACKGROUND 1
130 #define EFFECT_OUTLINE 2
131 #define EFFECT_OUTLINE_FAT 3
133 static int const pi_effects[] = { 1, 2, 3 };
134 static const char *const ppsz_effects_text[] = {
135 N_("Background"),N_("Outline"), N_("Fat Outline") };
136 static const int pi_color_values[] = {
137 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
138 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
139 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
141 static const char *const ppsz_color_descriptions[] = {
142 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
143 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
144 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
147 set_shortname( N_("Text renderer"))
148 set_description( N_("Freetype2 font renderer") )
149 set_category( CAT_VIDEO )
150 set_subcategory( SUBCAT_VIDEO_SUBPIC )
152 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
155 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
156 FONTSIZE_LONGTEXT, true )
158 /* opacity valid on 0..255, with default 255 = fully opaque */
159 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
160 OPACITY_TEXT, OPACITY_LONGTEXT, true )
162 /* hook to the color values list, with default 0x00ffffff = white */
163 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
164 COLOR_LONGTEXT, false )
165 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
167 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
168 FONTSIZER_LONGTEXT, false )
169 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
170 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
171 EFFECT_LONGTEXT, false )
172 change_integer_list( pi_effects, ppsz_effects_text, NULL )
174 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
175 YUVP_LONGTEXT, true )
176 set_capability( "text renderer", 100 )
177 add_shortcut( "text" )
178 set_callbacks( Create, Destroy )
183 /*****************************************************************************
185 *****************************************************************************/
187 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
188 static int RenderText( filter_t *, subpicture_region_t *,
189 subpicture_region_t * );
190 #ifdef HAVE_FONTCONFIG
191 static int RenderHtml( filter_t *, subpicture_region_t *,
192 subpicture_region_t * );
193 static char *FontConfig_Select( FcConfig *, const char *,
198 static int LoadFontsFromAttachments( filter_t *p_filter );
200 static int GetFontSize( filter_t *p_filter );
201 static int SetFontSize( filter_t *, int );
202 static void YUVFromRGB( uint32_t i_argb,
203 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
205 typedef struct line_desc_t line_desc_t;
208 /** NULL-terminated list of glyphs making the string */
209 FT_BitmapGlyph *pp_glyphs;
210 /** list of relative positions for the glyphs */
211 FT_Vector *p_glyph_pos;
212 /** list of RGB information for styled text
213 * -- if the rendering mode supports it (RenderYUVA) and
214 * b_new_color_mode is set, then it becomes possible to
215 * have multicoloured text within the subtitles. */
218 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
219 bool b_new_color_mode;
220 /** underline information -- only supplied if text should be underlined */
221 int *pi_underline_offset;
222 uint16_t *pi_underline_thickness;
226 int i_red, i_green, i_blue;
231 static line_desc_t *NewLine( int );
236 uint32_t i_font_color; /* ARGB */
237 uint32_t i_karaoke_bg_color; /* ARGB */
245 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
246 static void FreeLines( line_desc_t * );
247 static void FreeLine( line_desc_t * );
249 /*****************************************************************************
250 * filter_sys_t: freetype local data
251 *****************************************************************************
252 * This structure is part of the video output thread descriptor.
253 * It describes the freetype specific properties of an output thread.
254 *****************************************************************************/
257 FT_Library p_library; /* handle to library */
258 FT_Face p_face; /* handle to face object */
260 uint8_t i_font_opacity;
265 int i_default_font_size;
266 int i_display_height;
267 #ifdef HAVE_FONTCONFIG
268 char* psz_fontfamily;
272 input_attachment_t **pp_font_attachments;
273 int i_font_attachments;
277 #define UCHAR uint32_t
278 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
279 #define TR_FONT_STYLE_PTR ft_style_t *
281 #include "text_renderer.h"
283 /*****************************************************************************
284 * Create: allocates osd-text video thread output method
285 *****************************************************************************
286 * This function allocates and initializes a Clone vout method.
287 *****************************************************************************/
288 static int Create( vlc_object_t *p_this )
290 filter_t *p_filter = (filter_t *)p_this;
292 char *psz_fontfile=NULL;
293 char *psz_fontfamily=NULL;
294 int i_error,fontindex;
296 #ifdef HAVE_FONTCONFIG
297 FcPattern *fontpattern = NULL, *fontmatch = NULL;
298 /* Initialise result to Match, as fontconfig doesnt
299 * really set this other than some error-cases */
300 FcResult fontresult = FcResultMatch;
304 /* Allocate structure */
305 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
308 #ifdef HAVE_FONTCONFIG
309 p_sys->psz_fontfamily = NULL;
313 p_sys->p_library = 0;
314 p_sys->i_font_size = 0;
315 p_sys->i_display_height = 0;
317 var_Create( p_filter, "freetype-rel-fontsize",
318 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
320 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
321 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
322 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
323 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
324 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
325 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
326 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
329 if( !psz_fontfamily || !*psz_fontfamily )
331 free( psz_fontfamily );
332 #ifdef HAVE_FONTCONFIG
333 psz_fontfamily=strdup( DEFAULT_FONT );
335 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
336 if( !psz_fontfamily )
339 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
340 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
342 strcpy( psz_fontfamily, DEFAULT_FONT );
344 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
348 #ifdef HAVE_FONTCONFIG
349 /* Lets find some fontfile from freetype-font variable family */
351 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
355 dialog_progress_bar_t *p_dialog = NULL;
357 if( !FcConfigUptoDate( NULL ) )
359 p_dialog = dialog_ProgressCreate( p_filter,
360 _("Building font cache"),
361 _("Please wait while your font cache is rebuilt.\n"
362 "This should take less than a few minutes."), NULL );
364 char *path = xmalloc( PATH_MAX + 1 );
365 /* Fontconfig doesnt seem to know where windows fonts are with
366 * current contribs. So just tell default windows font directory
367 * is the place to search fonts
369 GetWindowsDirectory( path, PATH_MAX + 1 );
370 strcat( path, "\\fonts" );
372 dialog_ProgressSet( p_dialog, NULL, 0.4 );
374 FcConfigAppFontAddDir( NULL , path );
379 dialog_ProgressSet( p_dialog, NULL, 0.5 );
383 msg_Dbg( p_filter, "Building font database.");
385 FcConfigBuildFonts( NULL );
388 msg_Dbg( p_filter, "Finished building font database." );
389 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
391 fontpattern = FcPatternCreate();
395 msg_Err( p_filter, "Creating fontpattern failed");
401 dialog_ProgressSet( p_dialog, NULL, 0.7 );
403 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
404 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
405 free( psz_fontsize );
407 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
409 msg_Err( p_filter, "FontSubstitute failed");
412 FcDefaultSubstitute( fontpattern );
416 dialog_ProgressSet( p_dialog, NULL, 0.8 );
418 /* testing fontresult here doesn't do any good really, but maybe it will
419 * in future as fontconfig code doesn't set it in all cases and just
420 * returns NULL or doesn't set to to Match on all Match cases.*/
421 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
422 if( !fontmatch || fontresult == FcResultNoMatch )
424 msg_Err( p_filter, "Fontmatching failed");
428 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
429 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
432 msg_Err( p_filter, "Failed to get fontfile");
436 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
437 psz_fontfile ? psz_fontfile : "(null)" );
438 p_sys->psz_fontfamily = strdup( psz_fontfamily );
442 dialog_ProgressSet( p_dialog, NULL, 1.0 );
443 dialog_ProgressDestroy( p_dialog );
450 #ifdef HAVE_FONTCONFIG
451 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
452 psz_fontfile = psz_fontfamily;
457 i_error = FT_Init_FreeType( &p_sys->p_library );
460 msg_Err( p_filter, "couldn't initialize freetype" );
464 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
465 fontindex, &p_sys->p_face );
467 if( i_error == FT_Err_Unknown_File_Format )
469 msg_Err( p_filter, "file %s have unknown format",
470 psz_fontfile ? psz_fontfile : "(null)" );
475 msg_Err( p_filter, "failed to load font file %s",
476 psz_fontfile ? psz_fontfile : "(null)" );
480 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
483 msg_Err( p_filter, "font has no unicode translation table" );
487 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
489 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
492 p_sys->pp_font_attachments = NULL;
493 p_sys->i_font_attachments = 0;
495 p_filter->pf_render_text = RenderText;
496 #ifdef HAVE_FONTCONFIG
497 p_filter->pf_render_html = RenderHtml;
498 FcPatternDestroy( fontmatch );
499 FcPatternDestroy( fontpattern );
501 p_filter->pf_render_html = NULL;
504 free( psz_fontfamily );
505 LoadFontsFromAttachments( p_filter );
510 #ifdef HAVE_FONTCONFIG
511 if( fontmatch ) FcPatternDestroy( fontmatch );
512 if( fontpattern ) FcPatternDestroy( fontpattern );
517 dialog_ProgressDestroy( p_dialog );
520 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
521 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
522 free( psz_fontfamily );
527 /*****************************************************************************
528 * Destroy: destroy Clone video thread output method
529 *****************************************************************************
530 * Clean up all data and library connections
531 *****************************************************************************/
532 static void Destroy( vlc_object_t *p_this )
534 filter_t *p_filter = (filter_t *)p_this;
535 filter_sys_t *p_sys = p_filter->p_sys;
537 if( p_sys->pp_font_attachments )
541 for( k = 0; k < p_sys->i_font_attachments; k++ )
542 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
544 free( p_sys->pp_font_attachments );
547 #ifdef HAVE_FONTCONFIG
548 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
549 free( p_sys->psz_fontfamily );
552 /* FcFini asserts calling the subfunction FcCacheFini()
553 * even if no other library functions have been made since FcInit(),
554 * so don't call it. */
556 FT_Done_Face( p_sys->p_face );
557 FT_Done_FreeType( p_sys->p_library );
561 /*****************************************************************************
562 * Make any TTF/OTF fonts present in the attachments of the media file
563 * and store them for later use by the FreeType Engine
564 *****************************************************************************/
565 static int LoadFontsFromAttachments( filter_t *p_filter )
567 filter_sys_t *p_sys = p_filter->p_sys;
568 input_thread_t *p_input;
569 input_attachment_t **pp_attachments;
570 int i_attachments_cnt;
572 int rv = VLC_SUCCESS;
574 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
578 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
580 vlc_object_release(p_input);
584 p_sys->i_font_attachments = 0;
585 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
586 if(! p_sys->pp_font_attachments )
589 for( k = 0; k < i_attachments_cnt; k++ )
591 input_attachment_t *p_attach = pp_attachments[k];
593 if( p_sys->pp_font_attachments )
595 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
596 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
597 ( p_attach->i_data > 0 ) &&
598 ( p_attach->p_data != NULL ) )
600 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
604 vlc_input_attachment_Delete( p_attach );
609 vlc_input_attachment_Delete( p_attach );
612 free( pp_attachments );
614 vlc_object_release(p_input);
619 /*****************************************************************************
620 * Render: place string in picture
621 *****************************************************************************
622 * This function merges the previously rendered freetype glyphs into a picture
623 *****************************************************************************/
624 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
625 line_desc_t *p_line, int i_width, int i_height )
627 VLC_UNUSED(p_filter);
628 static const uint8_t pi_gamma[16] =
629 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
630 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
634 int i, x, y, i_pitch;
635 uint8_t i_y; /* YUV values, derived from incoming RGB */
638 /* Create a new subpicture region */
639 memset( &fmt, 0, sizeof(video_format_t) );
640 fmt.i_chroma = VLC_CODEC_YUVP;
641 fmt.i_width = fmt.i_visible_width = i_width + 4;
642 fmt.i_height = fmt.i_visible_height = i_height + 4;
643 if( p_region->fmt.i_visible_width > 0 )
644 fmt.i_visible_width = p_region->fmt.i_visible_width;
645 if( p_region->fmt.i_visible_height > 0 )
646 fmt.i_visible_height = p_region->fmt.i_visible_height;
647 fmt.i_x_offset = fmt.i_y_offset = 0;
649 assert( !p_region->p_picture );
650 p_region->p_picture = picture_NewFromFormat( &fmt );
651 if( !p_region->p_picture )
653 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
656 /* Calculate text color components */
657 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
658 25 * p_line->i_blue + 128) >> 8) + 16;
659 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
660 112 * p_line->i_blue + 128) >> 8) + 128;
661 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
662 18 * p_line->i_blue + 128) >> 8) + 128;
665 fmt.p_palette->i_entries = 16;
666 for( i = 0; i < 8; i++ )
668 fmt.p_palette->palette[i][0] = 0;
669 fmt.p_palette->palette[i][1] = 0x80;
670 fmt.p_palette->palette[i][2] = 0x80;
671 fmt.p_palette->palette[i][3] = pi_gamma[i];
672 fmt.p_palette->palette[i][3] =
673 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
675 for( i = 8; i < fmt.p_palette->i_entries; i++ )
677 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
678 fmt.p_palette->palette[i][1] = i_u;
679 fmt.p_palette->palette[i][2] = i_v;
680 fmt.p_palette->palette[i][3] = pi_gamma[i];
681 fmt.p_palette->palette[i][3] =
682 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
685 p_dst = p_region->p_picture->Y_PIXELS;
686 i_pitch = p_region->p_picture->Y_PITCH;
688 /* Initialize the region pixels */
689 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
691 for( ; p_line != NULL; p_line = p_line->p_next )
693 int i_glyph_tmax = 0;
694 int i_bitmap_offset, i_offset, i_align_offset = 0;
695 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
697 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
698 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
701 if( p_line->i_width < i_width )
703 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
705 i_align_offset = i_width - p_line->i_width;
707 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
709 i_align_offset = ( i_width - p_line->i_width ) / 2;
713 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
715 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
717 i_offset = ( p_line->p_glyph_pos[ i ].y +
718 i_glyph_tmax - p_glyph->top + 2 ) *
719 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
722 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
724 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
726 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
728 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
735 /* Outlining (find something better than nearest neighbour filtering ?) */
738 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
739 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
740 uint8_t left, current;
742 for( y = 1; y < (int)fmt.i_height - 1; y++ )
744 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
745 p_dst += p_region->p_picture->Y_PITCH;
748 for( x = 1; x < (int)fmt.i_width - 1; x++ )
751 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
752 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;
756 memset( p_top, 0, fmt.i_width );
762 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
763 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
764 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
765 int i_glyph_tmax, int i_align_offset,
766 uint8_t i_y, uint8_t i_u, uint8_t i_v,
767 subpicture_region_t *p_region)
771 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
773 p_dst_y = p_region->p_picture->Y_PIXELS;
774 p_dst_u = p_region->p_picture->U_PIXELS;
775 p_dst_v = p_region->p_picture->V_PIXELS;
776 p_dst_a = p_region->p_picture->A_PIXELS;
777 i_pitch = p_region->p_picture->A_PITCH;
779 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
780 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
782 for( y = 0; y < i_line_thickness; y++ )
784 int i_extra = p_this_glyph->bitmap.width;
788 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
789 (p_this_glyph_pos->x + p_this_glyph->left);
791 for( x = 0; x < i_extra; x++ )
795 /* break the underline around the tails of any glyphs which cross it */
796 /* Strikethrough doesn't get broken */
797 for( z = x - i_line_thickness;
798 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
801 if( p_next_glyph && ( z >= i_extra ) )
803 int i_row = i_line_offset + p_next_glyph->top + y;
805 if( ( p_next_glyph->bitmap.rows > i_row ) &&
806 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
811 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
813 int i_row = i_line_offset + p_this_glyph->top + y;
815 if( ( p_this_glyph->bitmap.rows > i_row ) &&
816 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
825 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
826 p_dst_u[i_offset+x] = i_u;
827 p_dst_v[i_offset+x] = i_v;
828 p_dst_a[i_offset+x] = 255;
835 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
837 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
838 int i_pitch = p_region->p_picture->A_PITCH;
841 for( ; p_line != NULL; p_line = p_line->p_next )
843 int i_glyph_tmax=0, i = 0;
844 int i_bitmap_offset, i_offset, i_align_offset = 0;
845 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
847 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
848 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
851 if( p_line->i_width < i_width )
853 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
855 i_align_offset = i_width - p_line->i_width;
857 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
859 i_align_offset = ( i_width - p_line->i_width ) / 2;
863 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
865 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
867 i_offset = ( p_line->p_glyph_pos[ i ].y +
868 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
869 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
870 i_align_offset +xoffset;
872 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
874 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
876 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
877 if( p_dst[i_offset+x] <
878 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
880 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
889 /*****************************************************************************
890 * Render: place string in picture
891 *****************************************************************************
892 * This function merges the previously rendered freetype glyphs into a picture
893 *****************************************************************************/
894 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
895 line_desc_t *p_line, int i_width, int i_height )
897 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
899 int i, x, y, i_pitch, i_alpha;
900 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
902 if( i_width == 0 || i_height == 0 )
905 /* Create a new subpicture region */
906 memset( &fmt, 0, sizeof(video_format_t) );
907 fmt.i_chroma = VLC_CODEC_YUVA;
908 fmt.i_width = fmt.i_visible_width = i_width + 6;
909 fmt.i_height = fmt.i_visible_height = i_height + 6;
910 if( p_region->fmt.i_visible_width > 0 )
911 fmt.i_visible_width = p_region->fmt.i_visible_width;
912 if( p_region->fmt.i_visible_height > 0 )
913 fmt.i_visible_height = p_region->fmt.i_visible_height;
914 fmt.i_x_offset = fmt.i_y_offset = 0;
916 p_region->p_picture = picture_NewFromFormat( &fmt );
917 if( !p_region->p_picture )
921 /* Calculate text color components */
922 YUVFromRGB( (p_line->i_red << 16) |
923 (p_line->i_green << 8) |
926 i_alpha = p_line->i_alpha;
928 p_dst_y = p_region->p_picture->Y_PIXELS;
929 p_dst_u = p_region->p_picture->U_PIXELS;
930 p_dst_v = p_region->p_picture->V_PIXELS;
931 p_dst_a = p_region->p_picture->A_PIXELS;
932 i_pitch = p_region->p_picture->A_PITCH;
934 /* Initialize the region pixels */
935 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
937 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
938 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
939 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
940 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
944 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
945 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
946 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
947 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
949 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
950 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
952 DrawBlack( p_line, i_width, p_region, 0, 0);
953 DrawBlack( p_line, i_width, p_region, -1, 0);
954 DrawBlack( p_line, i_width, p_region, 0, -1);
955 DrawBlack( p_line, i_width, p_region, 1, 0);
956 DrawBlack( p_line, i_width, p_region, 0, 1);
959 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
961 DrawBlack( p_line, i_width, p_region, -1, -1);
962 DrawBlack( p_line, i_width, p_region, -1, 1);
963 DrawBlack( p_line, i_width, p_region, 1, -1);
964 DrawBlack( p_line, i_width, p_region, 1, 1);
966 DrawBlack( p_line, i_width, p_region, -2, 0);
967 DrawBlack( p_line, i_width, p_region, 0, -2);
968 DrawBlack( p_line, i_width, p_region, 2, 0);
969 DrawBlack( p_line, i_width, p_region, 0, 2);
971 DrawBlack( p_line, i_width, p_region, -2, -2);
972 DrawBlack( p_line, i_width, p_region, -2, 2);
973 DrawBlack( p_line, i_width, p_region, 2, -2);
974 DrawBlack( p_line, i_width, p_region, 2, 2);
976 DrawBlack( p_line, i_width, p_region, -3, 0);
977 DrawBlack( p_line, i_width, p_region, 0, -3);
978 DrawBlack( p_line, i_width, p_region, 3, 0);
979 DrawBlack( p_line, i_width, p_region, 0, 3);
982 for( ; p_line != NULL; p_line = p_line->p_next )
984 int i_glyph_tmax = 0;
985 int i_bitmap_offset, i_offset, i_align_offset = 0;
986 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
988 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
989 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
992 if( p_line->i_width < i_width )
994 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
996 i_align_offset = i_width - p_line->i_width;
998 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1000 i_align_offset = ( i_width - p_line->i_width ) / 2;
1004 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1006 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1008 i_offset = ( p_line->p_glyph_pos[ i ].y +
1009 i_glyph_tmax - p_glyph->top + 3 ) *
1010 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1013 if( p_line->b_new_color_mode )
1015 /* Every glyph can (and in fact must) have its own color */
1016 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1019 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1021 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1023 uint8_t i_y_local = i_y;
1024 uint8_t i_u_local = i_u;
1025 uint8_t i_v_local = i_v;
1027 if( p_line->p_fg_bg_ratio != 0x00 )
1029 int i_split = p_glyph->bitmap.width *
1030 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1034 YUVFromRGB( p_line->p_bg_rgb[ i ],
1035 &i_y_local, &i_u_local, &i_v_local );
1039 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1041 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1042 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1044 p_dst_u[i_offset+x] = i_u;
1045 p_dst_v[i_offset+x] = i_v;
1047 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1048 p_dst_a[i_offset+x] = 0xff;
1051 i_offset += i_pitch;
1054 if( p_line->pi_underline_thickness[ i ] )
1056 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1057 p_line->pi_underline_offset[ i ],
1058 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1059 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1060 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1061 i_glyph_tmax, i_align_offset,
1068 /* Apply the alpha setting */
1069 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1070 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1076 * This function renders a text subpicture region into another one.
1077 * It also calculates the size needed for this string, and renders the
1078 * needed glyphs into memory. It is used as pf_add_string callback in
1079 * the vout method by this module
1081 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1082 subpicture_region_t *p_region_in )
1084 filter_sys_t *p_sys = p_filter->p_sys;
1085 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1086 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1087 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1088 int i_string_length;
1090 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1091 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1101 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1102 psz_string = p_region_in->psz_text;
1103 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1105 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1106 i_scale = val.i_int;
1108 if( p_region_in->p_style )
1110 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1111 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1112 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1116 i_font_color = p_sys->i_font_color;
1117 i_font_alpha = 255 - p_sys->i_font_opacity;
1118 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1121 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1122 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1123 SetFontSize( p_filter, i_font_size );
1125 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1126 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1127 i_blue = i_font_color & 0x000000FF;
1129 result.x = result.y = 0;
1130 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1132 psz_unicode = psz_unicode_orig =
1133 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1134 if( psz_unicode == NULL )
1136 #if defined(WORDS_BIGENDIAN)
1137 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1139 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1141 if( iconv_handle == (vlc_iconv_t)-1 )
1143 msg_Warn( p_filter, "unable to do conversion" );
1149 const char *p_in_buffer = psz_string;
1150 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1151 i_in_bytes = strlen( psz_string );
1152 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1153 i_out_bytes_left = i_out_bytes;
1154 p_out_buffer = (char *)psz_unicode;
1155 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1157 &p_out_buffer, &i_out_bytes_left );
1159 vlc_iconv_close( iconv_handle );
1163 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1164 "bytes left %u", (unsigned)i_in_bytes );
1167 *(uint32_t*)p_out_buffer = 0;
1168 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1171 #if defined(HAVE_FRIBIDI)
1173 uint32_t *p_fribidi_string;
1174 int32_t start_pos, pos = 0;
1176 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1177 if( !p_fribidi_string )
1180 /* Do bidi conversion line-by-line */
1181 while( pos < i_string_length )
1183 while( pos < i_string_length )
1185 i_char = psz_unicode[pos];
1186 if (i_char != '\r' && i_char != '\n')
1188 p_fribidi_string[pos] = i_char;
1192 while( pos < i_string_length )
1194 i_char = psz_unicode[pos];
1195 if (i_char == '\r' || i_char == '\n')
1199 if (pos > start_pos)
1201 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1202 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1205 (FriBidiChar*)p_fribidi_string + start_pos,
1210 free( psz_unicode_orig );
1211 psz_unicode = psz_unicode_orig = p_fribidi_string;
1212 p_fribidi_string[ i_string_length ] = 0;
1216 /* Calculate relative glyph positions and a bounding box for the
1218 if( !(p_line = NewLine( strlen( psz_string ))) )
1221 i_pen_x = i_pen_y = 0;
1223 psz_line_start = psz_unicode;
1225 #define face p_sys->p_face
1226 #define glyph face->glyph
1228 while( *psz_unicode )
1230 i_char = *psz_unicode++;
1231 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1236 if( i_char == '\n' )
1238 psz_line_start = psz_unicode;
1239 if( !(p_next = NewLine( strlen( psz_string ))) )
1241 p_line->p_next = p_next;
1242 p_line->i_width = line.xMax;
1243 p_line->i_height = face->size->metrics.height >> 6;
1244 p_line->pp_glyphs[ i ] = NULL;
1245 p_line->i_alpha = i_font_alpha;
1246 p_line->i_red = i_red;
1247 p_line->i_green = i_green;
1248 p_line->i_blue = i_blue;
1251 result.x = __MAX( result.x, line.xMax );
1252 result.y += face->size->metrics.height >> 6;
1255 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1256 i_pen_y += face->size->metrics.height >> 6;
1258 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1263 i_glyph_index = FT_Get_Char_Index( face, i_char );
1264 if( p_sys->i_use_kerning && i_glyph_index
1268 FT_Get_Kerning( face, i_previous, i_glyph_index,
1269 ft_kerning_default, &delta );
1270 i_pen_x += delta.x >> 6;
1273 p_line->p_glyph_pos[ i ].x = i_pen_x;
1274 p_line->p_glyph_pos[ i ].y = i_pen_y;
1275 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1278 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1282 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1285 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1289 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1290 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1293 FT_Done_Glyph( tmp_glyph );
1296 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1299 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1300 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1301 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1303 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1304 p_line->pp_glyphs[ i ] = NULL;
1306 p_line = NewLine( strlen( psz_string ));
1307 if( p_prev ) p_prev->p_next = p_line;
1308 else p_lines = p_line;
1310 uint32_t *psz_unicode_saved = psz_unicode;
1311 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1315 if( psz_unicode == psz_line_start )
1316 { /* try harder to break that line */
1317 psz_unicode = psz_unicode_saved;
1318 while( psz_unicode > psz_line_start &&
1319 *psz_unicode != '_' && *psz_unicode != '/' &&
1320 *psz_unicode != '\\' && *psz_unicode != '.' )
1325 if( psz_unicode == psz_line_start )
1327 msg_Warn( p_filter, "unbreakable string" );
1332 *psz_unicode = '\n';
1334 psz_unicode = psz_line_start;
1337 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1340 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1341 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1343 i_previous = i_glyph_index;
1344 i_pen_x += glyph->advance.x >> 6;
1348 p_line->i_width = line.xMax;
1349 p_line->i_height = face->size->metrics.height >> 6;
1350 p_line->pp_glyphs[ i ] = NULL;
1351 p_line->i_alpha = i_font_alpha;
1352 p_line->i_red = i_red;
1353 p_line->i_green = i_green;
1354 p_line->i_blue = i_blue;
1355 result.x = __MAX( result.x, line.xMax );
1356 result.y += line.yMax - line.yMin;
1361 p_region_out->i_x = p_region_in->i_x;
1362 p_region_out->i_y = p_region_in->i_y;
1364 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1365 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1367 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1369 free( psz_unicode_orig );
1370 FreeLines( p_lines );
1374 free( psz_unicode_orig );
1375 FreeLines( p_lines );
1376 return VLC_EGENERIC;
1379 #ifdef HAVE_FONTCONFIG
1380 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1381 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1382 bool b_italic, bool b_uline, bool b_through )
1384 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1388 p_style->i_font_size = i_font_size;
1389 p_style->i_font_color = i_font_color;
1390 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1391 p_style->b_italic = b_italic;
1392 p_style->b_bold = b_bold;
1393 p_style->b_underline = b_uline;
1394 p_style->b_through = b_through;
1396 p_style->psz_fontname = strdup( psz_fontname );
1401 static void DeleteStyle( ft_style_t *p_style )
1405 free( p_style->psz_fontname );
1410 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1417 if(( s1->i_font_size == s2->i_font_size ) &&
1418 ( s1->i_font_color == s2->i_font_color ) &&
1419 ( s1->b_italic == s2->b_italic ) &&
1420 ( s1->b_through == s2->b_through ) &&
1421 ( s1->b_bold == s2->b_bold ) &&
1422 ( s1->b_underline == s2->b_underline ) &&
1423 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1430 static void IconvText( filter_t *p_filter, const char *psz_string,
1431 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1433 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1435 /* If memory hasn't been allocated for our output string, allocate it here
1436 * - the calling function must now be responsible for freeing it.
1438 if( !*ppsz_unicode )
1439 *ppsz_unicode = (uint32_t *)
1440 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1442 /* We don't need to handle a NULL pointer in *ppsz_unicode
1443 * if we are instead testing for a non NULL value like we are here */
1447 #if defined(WORDS_BIGENDIAN)
1448 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1450 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1452 if( iconv_handle != (vlc_iconv_t)-1 )
1454 char *p_in_buffer, *p_out_buffer;
1455 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1456 i_in_bytes = strlen( psz_string );
1457 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1458 i_out_bytes_left = i_out_bytes;
1459 p_in_buffer = (char *) psz_string;
1460 p_out_buffer = (char *) *ppsz_unicode;
1461 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1462 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1464 vlc_iconv_close( iconv_handle );
1468 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1469 "bytes left %u", (unsigned)i_in_bytes );
1473 *(uint32_t*)p_out_buffer = 0;
1475 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1480 msg_Warn( p_filter, "unable to do conversion" );
1485 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1486 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1487 bool b_uline, bool b_through )
1489 ft_style_t *p_style = NULL;
1491 char *psz_fontname = NULL;
1492 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1493 uint32_t i_karaoke_bg_color = i_font_color;
1494 int i_font_size = p_sys->i_font_size;
1496 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1497 &i_font_color, &i_karaoke_bg_color ))
1499 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1500 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1505 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1506 bool b_uline, bool b_through, bool b_bold,
1507 bool b_italic, int i_karaoke_bgcolor,
1508 line_desc_t *p_line, uint32_t *psz_unicode,
1509 int *pi_pen_x, int i_pen_y, int *pi_start,
1510 FT_Vector *p_result )
1515 bool b_first_on_line = true;
1518 int i_pen_x_start = *pi_pen_x;
1520 uint32_t *psz_unicode_start = psz_unicode;
1522 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1524 /* Account for part of line already in position */
1525 for( i=0; i<*pi_start; i++ )
1529 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1530 ft_glyph_bbox_pixels, &glyph_size );
1532 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1533 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1534 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1535 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1541 b_first_on_line = false;
1543 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1549 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1550 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1554 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1555 ft_kerning_default, &delta );
1556 *pi_pen_x += delta.x >> 6;
1558 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1559 p_line->p_glyph_pos[ i ].y = i_pen_y;
1561 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1565 "unable to render text FT_Load_Glyph returned %d", i_error );
1566 p_line->pp_glyphs[ i ] = NULL;
1567 return VLC_EGENERIC;
1570 /* Do synthetic styling now that Freetype supports it;
1571 * ie. if the font we have loaded is NOT already in the
1572 * style that the tags want, then switch it on; if they
1573 * are then don't. */
1574 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1575 FT_GlyphSlot_Embolden( p_face->glyph );
1576 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1577 FT_GlyphSlot_Oblique( p_face->glyph );
1579 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1583 "unable to render text FT_Get_Glyph returned %d", i_error );
1584 p_line->pp_glyphs[ i ] = NULL;
1585 return VLC_EGENERIC;
1587 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1588 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1591 FT_Done_Glyph( tmp_glyph );
1594 if( b_uline || b_through )
1596 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1597 p_face->size->metrics.y_scale));
1598 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1599 p_face->size->metrics.y_scale));
1601 p_line->pi_underline_offset[ i ] =
1602 ( aOffset < 0 ) ? -aOffset : aOffset;
1603 p_line->pi_underline_thickness[ i ] =
1604 ( aSize < 0 ) ? -aSize : aSize;
1607 /* Move the baseline to make it strikethrough instead of
1608 * underline. That means that strikethrough takes precedence
1610 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1611 p_face->size->metrics.y_scale));
1613 p_line->pi_underline_offset[ i ] -=
1614 ( aDescent < 0 ) ? -aDescent : aDescent;
1618 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1619 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1620 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1621 p_line->p_fg_bg_ratio[ i ] = 0x00;
1623 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1624 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1625 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1627 for( ; i >= *pi_start; i-- )
1628 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1631 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1635 if( psz_unicode == psz_unicode_start )
1637 if( b_first_on_line )
1639 msg_Warn( p_filter, "unbreakable string" );
1640 p_line->pp_glyphs[ i ] = NULL;
1641 return VLC_EGENERIC;
1643 *pi_pen_x = i_pen_x_start;
1645 p_line->i_width = line.xMax;
1646 p_line->i_height = __MAX( p_line->i_height,
1647 p_face->size->metrics.height >> 6 );
1648 p_line->pp_glyphs[ i ] = NULL;
1650 p_result->x = __MAX( p_result->x, line.xMax );
1651 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1652 i_yMax - i_yMin ) );
1657 *psz_unicode = '\n';
1659 psz_unicode = psz_unicode_start;
1660 *pi_pen_x = i_pen_x_start;
1668 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1669 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1671 i_previous = i_glyph_index;
1672 *pi_pen_x += p_face->glyph->advance.x >> 6;
1675 p_line->i_width = line.xMax;
1676 p_line->i_height = __MAX( p_line->i_height,
1677 p_face->size->metrics.height >> 6 );
1678 p_line->pp_glyphs[ i ] = NULL;
1680 p_result->x = __MAX( p_result->x, line.xMax );
1681 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1682 line.yMax - line.yMin ) );
1686 /* Get rid of any text processed - if necessary repositioning
1687 * at the start of a new line of text
1691 *psz_unicode_start = '\0';
1693 else if( psz_unicode > psz_unicode_start )
1695 for( i=0; psz_unicode[ i ]; i++ )
1696 psz_unicode_start[ i ] = psz_unicode[ i ];
1697 psz_unicode_start[ i ] = '\0';
1703 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1704 uint32_t **psz_text_out, uint32_t *pi_runs,
1705 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1706 ft_style_t *p_style )
1708 uint32_t i_string_length = 0;
1710 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1711 *psz_text_out += i_string_length;
1713 if( ppp_styles && ppi_run_lengths )
1717 /* XXX this logic looks somewhat broken */
1721 *ppp_styles = realloc_or_free( *ppp_styles,
1722 *pi_runs * sizeof( ft_style_t * ) );
1724 else if( *pi_runs == 1 )
1726 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1729 /* We have just malloc'ed this memory successfully -
1730 * *pi_runs HAS to be within the memory area of *ppp_styles */
1733 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1737 /* XXX more iffy logic */
1739 if( *ppi_run_lengths )
1741 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1742 *pi_runs * sizeof( uint32_t ) );
1744 else if( *pi_runs == 1 )
1746 *ppi_run_lengths = (uint32_t *)
1747 malloc( *pi_runs * sizeof( uint32_t ) );
1750 /* same remarks here */
1751 if( *ppi_run_lengths )
1753 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1756 /* If we couldn't use the p_style argument due to memory allocation
1757 * problems above, release it here.
1759 if( p_style ) DeleteStyle( p_style );
1762 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1766 for( k=0; k < p_sys->i_font_attachments; k++ )
1768 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1770 FT_Face p_face = NULL;
1772 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1780 bool match = !strcasecmp( p_face->family_name,
1781 p_style->psz_fontname );
1783 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1784 match = match && p_style->b_bold;
1786 match = match && !p_style->b_bold;
1788 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1789 match = match && p_style->b_italic;
1791 match = match && !p_style->b_italic;
1799 FT_Done_Face( p_face );
1804 return VLC_EGENERIC;
1807 static int ProcessLines( filter_t *p_filter,
1812 uint32_t *pi_run_lengths,
1813 ft_style_t **pp_styles,
1814 line_desc_t **pp_lines,
1816 FT_Vector *p_result,
1820 uint32_t *pi_k_run_lengths,
1821 uint32_t *pi_k_durations )
1823 filter_sys_t *p_sys = p_filter->p_sys;
1824 ft_style_t **pp_char_styles;
1825 int *p_new_positions = NULL;
1826 int8_t *p_levels = NULL;
1827 uint8_t *pi_karaoke_bar = NULL;
1831 /* Assign each character in the text string its style explicitly, so that
1832 * after the characters have been shuffled around by Fribidi, we can re-apply
1833 * the styles, and to simplify the calculation of runs within a line.
1835 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1836 if( !pp_char_styles )
1841 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1842 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1843 * we just won't be able to display the progress bar; at least we'll
1849 for( j = 0; j < i_runs; j++ )
1850 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1851 pp_char_styles[ i++ ] = pp_styles[ j ];
1853 #if defined(HAVE_FRIBIDI)
1855 ft_style_t **pp_char_styles_new;
1856 int *p_old_positions;
1857 uint32_t *p_fribidi_string;
1858 int start_pos, pos = 0;
1860 pp_char_styles_new = (ft_style_t **)
1861 malloc( i_len * sizeof( ft_style_t * ));
1863 p_fribidi_string = (uint32_t *)
1864 malloc( (i_len + 1) * sizeof(uint32_t) );
1865 p_old_positions = (int *)
1866 malloc( (i_len + 1) * sizeof( int ) );
1867 p_new_positions = (int *)
1868 malloc( (i_len + 1) * sizeof( int ) );
1869 p_levels = (int8_t *)
1870 malloc( (i_len + 1) * sizeof( int8_t ) );
1872 if( ! pp_char_styles_new ||
1873 ! p_fribidi_string ||
1874 ! p_old_positions ||
1875 ! p_new_positions ||
1879 free( p_old_positions );
1880 free( p_new_positions );
1881 free( p_fribidi_string );
1882 free( pp_char_styles_new );
1883 free( pi_karaoke_bar );
1885 free( pp_char_styles );
1889 /* Do bidi conversion line-by-line */
1892 while(pos < i_len) {
1893 if (psz_text[pos] != '\n')
1895 p_fribidi_string[pos] = psz_text[pos];
1896 pp_char_styles_new[pos] = pp_char_styles[pos];
1897 p_new_positions[pos] = pos;
1902 while(pos < i_len) {
1903 if (psz_text[pos] == '\n')
1907 if (pos > start_pos)
1909 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1910 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1911 pos - start_pos, &base_dir,
1912 (FriBidiChar*)p_fribidi_string + start_pos,
1913 p_new_positions + start_pos,
1915 p_levels + start_pos );
1916 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1918 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1919 p_old_positions[ j - start_pos ] ];
1920 p_new_positions[ j ] += start_pos;
1924 free( p_old_positions );
1925 free( pp_char_styles );
1926 pp_char_styles = pp_char_styles_new;
1927 psz_text = p_fribidi_string;
1928 p_fribidi_string[ i_len ] = 0;
1931 /* Work out the karaoke */
1932 if( pi_karaoke_bar )
1934 int64_t i_last_duration = 0;
1935 int64_t i_duration = 0;
1936 int64_t i_start_pos = 0;
1937 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1939 for( k = 0; k< i_k_runs; k++ )
1941 double fraction = 0.0;
1943 i_duration += pi_k_durations[ k ];
1945 if( i_duration < i_elapsed )
1947 /* Completely finished this run-length -
1948 * let it render normally */
1952 else if( i_elapsed < i_last_duration )
1954 /* Haven't got up to this segment yet -
1955 * render it completely in karaoke BG mode */
1961 /* Partway through this run */
1963 fraction = (double)(i_elapsed - i_last_duration) /
1964 (double)pi_k_durations[ k ];
1966 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1968 double shade = pi_k_run_lengths[ k ] * fraction;
1970 if( p_new_positions )
1971 j = p_new_positions[ i_start_pos + i ];
1973 j = i_start_pos + i;
1975 if( i < (uint32_t)shade )
1976 pi_karaoke_bar[ j ] = 0xff;
1977 else if( (double)i > shade )
1978 pi_karaoke_bar[ j ] = 0x00;
1981 shade -= (int)shade;
1982 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1983 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1987 i_last_duration = i_duration;
1988 i_start_pos += pi_k_run_lengths[ k ];
1992 free( p_new_positions );
1994 FT_Vector tmp_result;
1996 line_desc_t *p_line = NULL;
1997 line_desc_t *p_prev = NULL;
2003 p_result->x = p_result->y = 0;
2004 tmp_result.x = tmp_result.y = 0;
2007 for( k = 0; k <= (uint32_t) i_len; k++ )
2009 if( ( k == (uint32_t) i_len ) ||
2011 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2013 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2015 /* End of the current style run */
2016 FT_Face p_face = NULL;
2019 /* Look for a match amongst our attachments first */
2020 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2024 char *psz_fontfile = NULL;
2026 psz_fontfile = FontConfig_Select( NULL,
2027 p_style->psz_fontname,
2031 if( psz_fontfile && ! *psz_fontfile )
2033 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2034 " so using default font", p_style->psz_fontname,
2035 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2036 (p_style->b_bold ? "(Bold)" :
2037 (p_style->b_italic ? "(Italic)" : ""))) );
2038 free( psz_fontfile );
2039 psz_fontfile = NULL;
2044 if( FT_New_Face( p_sys->p_library,
2045 psz_fontfile, i_idx, &p_face ) )
2047 free( psz_fontfile );
2048 free( pp_char_styles );
2049 #if defined(HAVE_FRIBIDI)
2052 free( pi_karaoke_bar );
2053 return VLC_EGENERIC;
2055 free( psz_fontfile );
2059 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2061 /* We've loaded a font face which is unhelpful for actually
2062 * rendering text - fallback to the default one.
2064 FT_Done_Face( p_face );
2068 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2069 ft_encoding_unicode ) ||
2070 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2071 p_style->i_font_size ) )
2073 if( p_face ) FT_Done_Face( p_face );
2074 free( pp_char_styles );
2075 #if defined(HAVE_FRIBIDI)
2078 free( pi_karaoke_bar );
2079 return VLC_EGENERIC;
2081 p_sys->i_use_kerning =
2082 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2085 uint32_t *psz_unicode = (uint32_t *)
2086 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2089 if( p_face ) FT_Done_Face( p_face );
2090 free( pp_char_styles );
2091 free( psz_unicode );
2092 #if defined(HAVE_FRIBIDI)
2095 free( pi_karaoke_bar );
2098 memcpy( psz_unicode, psz_text + i_prev,
2099 sizeof( uint32_t ) * ( k - i_prev ) );
2100 psz_unicode[ k - i_prev ] = 0;
2101 while( *psz_unicode )
2105 if( !(p_line = NewLine( i_len - i_prev)) )
2107 if( p_face ) FT_Done_Face( p_face );
2108 free( pp_char_styles );
2109 free( psz_unicode );
2110 #if defined(HAVE_FRIBIDI)
2113 free( pi_karaoke_bar );
2116 /* New Color mode only works in YUVA rendering mode --
2117 * (RGB mode has palette constraints on it). We therefore
2118 * need to populate the legacy colour fields also.
2120 p_line->b_new_color_mode = true;
2121 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2122 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2123 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2124 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2125 p_line->p_next = NULL;
2127 i_pen_y += tmp_result.y;
2131 if( p_prev ) p_prev->p_next = p_line;
2132 else *pp_lines = p_line;
2135 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2136 p_style->i_font_color, p_style->b_underline,
2140 p_style->i_karaoke_bg_color,
2141 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2142 &tmp_result ) != VLC_SUCCESS )
2144 if( p_face ) FT_Done_Face( p_face );
2145 free( pp_char_styles );
2146 free( psz_unicode );
2147 #if defined(HAVE_FRIBIDI)
2150 free( pi_karaoke_bar );
2151 return VLC_EGENERIC;
2156 p_result->x = __MAX( p_result->x, tmp_result.x );
2157 p_result->y += tmp_result.y;
2162 if( *psz_unicode == '\n')
2166 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2168 *c_ptr = *(c_ptr+1);
2173 free( psz_unicode );
2174 if( p_face ) FT_Done_Face( p_face );
2178 free( pp_char_styles );
2179 #if defined(HAVE_FRIBIDI)
2184 p_result->x = __MAX( p_result->x, tmp_result.x );
2185 p_result->y += tmp_result.y;
2188 if( pi_karaoke_bar )
2191 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2193 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2195 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2199 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2201 /* 100% BG colour will render faster if we
2202 * instead make it 100% FG colour, so leave
2203 * the ratio alone and copy the value across
2205 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2209 if( pi_karaoke_bar[ i ] & 0x80 )
2211 /* Swap Left and Right sides over for Right aligned
2212 * language text (eg. Arabic, Hebrew)
2214 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2216 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2217 p_line->p_bg_rgb[ k ] = i_tmp;
2219 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2222 /* Jump over the '\n' at the line-end */
2225 free( pi_karaoke_bar );
2231 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2232 subpicture_region_t *p_region_in )
2234 int rv = VLC_SUCCESS;
2235 stream_t *p_sub = NULL;
2236 xml_reader_t *p_xml_reader = NULL;
2238 if( !p_region_in || !p_region_in->psz_html )
2239 return VLC_EGENERIC;
2241 /* Reset the default fontsize in case screen metrics have changed */
2242 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2244 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2245 (uint8_t *) p_region_in->psz_html,
2246 strlen( p_region_in->psz_html ),
2250 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2251 if( p_filter->p_sys->p_xml )
2253 bool b_karaoke = false;
2255 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2258 /* Look for Root Node */
2259 if( xml_ReaderRead( p_xml_reader ) == 1 )
2261 char *psz_node = xml_ReaderName( p_xml_reader );
2263 if( !strcasecmp( "karaoke", psz_node ) )
2265 /* We're going to have to render the text a number
2266 * of times to show the progress marker on the text.
2268 var_SetBool( p_filter, "text-rerender", true );
2271 else if( !strcasecmp( "text", psz_node ) )
2277 /* Only text and karaoke tags are supported */
2278 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2279 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2280 p_xml_reader = NULL;
2292 uint32_t i_runs = 0;
2293 uint32_t i_k_runs = 0;
2294 uint32_t *pi_run_lengths = NULL;
2295 uint32_t *pi_k_run_lengths = NULL;
2296 uint32_t *pi_k_durations = NULL;
2297 ft_style_t **pp_styles = NULL;
2299 line_desc_t *p_lines = NULL;
2301 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2302 sizeof( uint32_t ) );
2307 rv = ProcessNodes( p_filter, p_xml_reader,
2308 p_region_in->p_style, psz_text, &i_len,
2309 &i_runs, &pi_run_lengths, &pp_styles,
2311 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2314 p_region_out->i_x = p_region_in->i_x;
2315 p_region_out->i_y = p_region_in->i_y;
2317 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2319 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2320 pi_run_lengths, pp_styles, &p_lines, &result,
2321 b_karaoke, i_k_runs, pi_k_run_lengths,
2325 for( k=0; k<i_runs; k++)
2326 DeleteStyle( pp_styles[k] );
2328 free( pi_run_lengths );
2331 /* Don't attempt to render text that couldn't be layed out
2334 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2336 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2338 Render( p_filter, p_region_out, p_lines,
2339 result.x, result.y );
2343 RenderYUVA( p_filter, p_region_out, p_lines,
2344 result.x, result.y );
2348 FreeLines( p_lines );
2350 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2353 stream_Delete( p_sub );
2359 static char* FontConfig_Select( FcConfig* priv, const char* family,
2360 bool b_bold, bool b_italic, int *i_idx )
2363 FcPattern *pat, *p_pat;
2367 pat = FcPatternCreate();
2368 if (!pat) return NULL;
2370 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2371 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2372 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2373 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2375 FcDefaultSubstitute( pat );
2377 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2379 FcPatternDestroy( pat );
2383 p_pat = FcFontMatch( priv, pat, &result );
2384 FcPatternDestroy( pat );
2385 if( !p_pat ) return NULL;
2387 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2388 || ( val_b != FcTrue ) )
2390 FcPatternDestroy( p_pat );
2393 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2398 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2400 FcPatternDestroy( p_pat );
2405 if( strcasecmp((const char*)val_s, family ) != 0 )
2406 msg_Warn( p_filter, "fontconfig: selected font family is not"
2407 "the requested one: '%s' != '%s'\n",
2408 (const char*)val_s, family );
2411 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2413 FcPatternDestroy( p_pat );
2417 FcPatternDestroy( p_pat );
2418 return strdup( (const char*)val_s );
2422 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2423 uint32_t **psz_text_out, uint32_t *pi_runs,
2424 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2425 ft_style_t *p_style )
2427 VLC_UNUSED(p_filter);
2428 VLC_UNUSED(psz_text_in);
2429 VLC_UNUSED(psz_text_out);
2430 VLC_UNUSED(pi_runs);
2431 VLC_UNUSED(ppi_run_lengths);
2432 VLC_UNUSED(ppp_styles);
2433 VLC_UNUSED(p_style);
2436 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2437 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2438 bool b_uline, bool b_through )
2441 VLC_UNUSED(p_fonts);
2443 VLC_UNUSED(b_italic);
2444 VLC_UNUSED(b_uline);
2445 VLC_UNUSED(b_through);
2450 static void FreeLine( line_desc_t *p_line )
2453 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2455 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2457 free( p_line->pp_glyphs );
2458 free( p_line->p_glyph_pos );
2459 free( p_line->p_fg_rgb );
2460 free( p_line->p_bg_rgb );
2461 free( p_line->p_fg_bg_ratio );
2462 free( p_line->pi_underline_offset );
2463 free( p_line->pi_underline_thickness );
2467 static void FreeLines( line_desc_t *p_lines )
2469 line_desc_t *p_line, *p_next;
2471 if( !p_lines ) return;
2473 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2475 p_next = p_line->p_next;
2480 static line_desc_t *NewLine( int i_count )
2482 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2484 if( !p_line ) return NULL;
2485 p_line->i_height = 0;
2486 p_line->i_width = 0;
2487 p_line->p_next = NULL;
2489 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2490 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2491 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2492 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2493 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2494 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2495 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2496 if( ( p_line->pp_glyphs == NULL ) ||
2497 ( p_line->p_glyph_pos == NULL ) ||
2498 ( p_line->p_fg_rgb == NULL ) ||
2499 ( p_line->p_bg_rgb == NULL ) ||
2500 ( p_line->p_fg_bg_ratio == NULL ) ||
2501 ( p_line->pi_underline_offset == NULL ) ||
2502 ( p_line->pi_underline_thickness == NULL ) )
2504 free( p_line->pi_underline_thickness );
2505 free( p_line->pi_underline_offset );
2506 free( p_line->p_fg_rgb );
2507 free( p_line->p_bg_rgb );
2508 free( p_line->p_fg_bg_ratio );
2509 free( p_line->p_glyph_pos );
2510 free( p_line->pp_glyphs );
2514 p_line->pp_glyphs[0] = NULL;
2515 p_line->b_new_color_mode = false;
2520 static int GetFontSize( filter_t *p_filter )
2522 filter_sys_t *p_sys = p_filter->p_sys;
2526 if( p_sys->i_default_font_size )
2528 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2529 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2531 i_size = p_sys->i_default_font_size;
2535 var_Get( p_filter, "freetype-rel-fontsize", &val );
2538 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2539 p_filter->p_sys->i_display_height =
2540 p_filter->fmt_out.video.i_height;
2545 msg_Warn( p_filter, "invalid fontsize, using 12" );
2546 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2547 i_size = 12 * val.i_int / 1000;
2554 static int SetFontSize( filter_t *p_filter, int i_size )
2556 filter_sys_t *p_sys = p_filter->p_sys;
2560 i_size = GetFontSize( p_filter );
2562 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2565 p_sys->i_font_size = i_size;
2567 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2569 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2570 return VLC_EGENERIC;
2576 static void YUVFromRGB( uint32_t i_argb,
2577 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2579 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2580 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2581 int i_blue = ( i_argb & 0x000000ff );
2583 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2584 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2585 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2586 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2587 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2588 -585 * i_blue + 4096 + 1048576) >> 13, 240);