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>
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, int i_karaoke_bgcolor,
1494 line_desc_t *p_line, uint32_t *psz_unicode,
1495 int *pi_pen_x, int i_pen_y, int *pi_start,
1496 FT_Vector *p_result )
1501 bool b_first_on_line = true;
1504 int i_pen_x_start = *pi_pen_x;
1506 uint32_t *psz_unicode_start = psz_unicode;
1508 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1510 /* Account for part of line already in position */
1511 for( i=0; i<*pi_start; i++ )
1515 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1516 ft_glyph_bbox_pixels, &glyph_size );
1518 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1519 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1520 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1521 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1527 b_first_on_line = false;
1529 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1535 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1536 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1540 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1541 ft_kerning_default, &delta );
1542 *pi_pen_x += delta.x >> 6;
1544 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1545 p_line->p_glyph_pos[ i ].y = i_pen_y;
1547 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1551 "unable to render text FT_Load_Glyph returned %d", i_error );
1552 p_line->pp_glyphs[ i ] = NULL;
1553 return VLC_EGENERIC;
1555 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1559 "unable to render text FT_Get_Glyph returned %d", i_error );
1560 p_line->pp_glyphs[ i ] = NULL;
1561 return VLC_EGENERIC;
1563 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1564 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1567 FT_Done_Glyph( tmp_glyph );
1570 if( b_uline || b_through )
1572 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1573 p_face->size->metrics.y_scale));
1574 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1575 p_face->size->metrics.y_scale));
1577 p_line->pi_underline_offset[ i ] =
1578 ( aOffset < 0 ) ? -aOffset : aOffset;
1579 p_line->pi_underline_thickness[ i ] =
1580 ( aSize < 0 ) ? -aSize : aSize;
1583 /* Move the baseline to make it strikethrough instead of
1584 * underline. That means that strikethrough takes precedence
1586 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1587 p_face->size->metrics.y_scale));
1589 p_line->pi_underline_offset[ i ] -=
1590 ( aDescent < 0 ) ? -aDescent : aDescent;
1594 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1595 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1596 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1597 p_line->p_fg_bg_ratio[ i ] = 0x00;
1599 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1600 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1601 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1603 for( ; i >= *pi_start; i-- )
1604 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1607 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1611 if( psz_unicode == psz_unicode_start )
1613 if( b_first_on_line )
1615 msg_Warn( p_filter, "unbreakable string" );
1616 p_line->pp_glyphs[ i ] = NULL;
1617 return VLC_EGENERIC;
1619 *pi_pen_x = i_pen_x_start;
1621 p_line->i_width = line.xMax;
1622 p_line->i_height = __MAX( p_line->i_height,
1623 p_face->size->metrics.height >> 6 );
1624 p_line->pp_glyphs[ i ] = NULL;
1626 p_result->x = __MAX( p_result->x, line.xMax );
1627 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1628 i_yMax - i_yMin ) );
1633 *psz_unicode = '\n';
1635 psz_unicode = psz_unicode_start;
1636 *pi_pen_x = i_pen_x_start;
1644 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1645 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1647 i_previous = i_glyph_index;
1648 *pi_pen_x += p_face->glyph->advance.x >> 6;
1651 p_line->i_width = line.xMax;
1652 p_line->i_height = __MAX( p_line->i_height,
1653 p_face->size->metrics.height >> 6 );
1654 p_line->pp_glyphs[ i ] = NULL;
1656 p_result->x = __MAX( p_result->x, line.xMax );
1657 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1658 line.yMax - line.yMin ) );
1662 /* Get rid of any text processed - if necessary repositioning
1663 * at the start of a new line of text
1667 *psz_unicode_start = '\0';
1669 else if( psz_unicode > psz_unicode_start )
1671 for( i=0; psz_unicode[ i ]; i++ )
1672 psz_unicode_start[ i ] = psz_unicode[ i ];
1673 psz_unicode_start[ i ] = '\0';
1679 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1680 uint32_t **psz_text_out, uint32_t *pi_runs,
1681 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1682 ft_style_t *p_style )
1684 uint32_t i_string_length = 0;
1686 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1687 *psz_text_out += i_string_length;
1689 if( ppp_styles && ppi_run_lengths )
1693 /* XXX this logic looks somewhat broken */
1697 *ppp_styles = realloc_or_free( *ppp_styles,
1698 *pi_runs * sizeof( ft_style_t * ) );
1700 else if( *pi_runs == 1 )
1702 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1705 /* We have just malloc'ed this memory successfully -
1706 * *pi_runs HAS to be within the memory area of *ppp_styles */
1709 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1713 /* XXX more iffy logic */
1715 if( *ppi_run_lengths )
1717 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1718 *pi_runs * sizeof( uint32_t ) );
1720 else if( *pi_runs == 1 )
1722 *ppi_run_lengths = (uint32_t *)
1723 malloc( *pi_runs * sizeof( uint32_t ) );
1726 /* same remarks here */
1727 if( *ppi_run_lengths )
1729 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1732 /* If we couldn't use the p_style argument due to memory allocation
1733 * problems above, release it here.
1735 if( p_style ) DeleteStyle( p_style );
1738 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1742 for( k=0; k < p_sys->i_font_attachments; k++ )
1744 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1746 FT_Face p_face = NULL;
1748 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1756 bool match = !strcasecmp( p_face->family_name,
1757 p_style->psz_fontname );
1759 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1760 match = match && p_style->b_bold;
1762 match = match && !p_style->b_bold;
1764 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1765 match = match && p_style->b_italic;
1767 match = match && !p_style->b_italic;
1775 FT_Done_Face( p_face );
1780 return VLC_EGENERIC;
1783 static int ProcessLines( filter_t *p_filter,
1788 uint32_t *pi_run_lengths,
1789 ft_style_t **pp_styles,
1790 line_desc_t **pp_lines,
1792 FT_Vector *p_result,
1796 uint32_t *pi_k_run_lengths,
1797 uint32_t *pi_k_durations )
1799 filter_sys_t *p_sys = p_filter->p_sys;
1800 ft_style_t **pp_char_styles;
1801 int *p_new_positions = NULL;
1802 int8_t *p_levels = NULL;
1803 uint8_t *pi_karaoke_bar = NULL;
1807 /* Assign each character in the text string its style explicitly, so that
1808 * after the characters have been shuffled around by Fribidi, we can re-apply
1809 * the styles, and to simplify the calculation of runs within a line.
1811 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1812 if( !pp_char_styles )
1817 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1818 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1819 * we just won't be able to display the progress bar; at least we'll
1825 for( j = 0; j < i_runs; j++ )
1826 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1827 pp_char_styles[ i++ ] = pp_styles[ j ];
1829 #if defined(HAVE_FRIBIDI)
1831 ft_style_t **pp_char_styles_new;
1832 int *p_old_positions;
1833 uint32_t *p_fribidi_string;
1834 int start_pos, pos = 0;
1836 pp_char_styles_new = (ft_style_t **)
1837 malloc( i_len * sizeof( ft_style_t * ));
1839 p_fribidi_string = (uint32_t *)
1840 malloc( (i_len + 1) * sizeof(uint32_t) );
1841 p_old_positions = (int *)
1842 malloc( (i_len + 1) * sizeof( int ) );
1843 p_new_positions = (int *)
1844 malloc( (i_len + 1) * sizeof( int ) );
1845 p_levels = (int8_t *)
1846 malloc( (i_len + 1) * sizeof( int8_t ) );
1848 if( ! pp_char_styles_new ||
1849 ! p_fribidi_string ||
1850 ! p_old_positions ||
1851 ! p_new_positions ||
1855 free( p_old_positions );
1856 free( p_new_positions );
1857 free( p_fribidi_string );
1858 free( pp_char_styles_new );
1859 free( pi_karaoke_bar );
1861 free( pp_char_styles );
1865 /* Do bidi conversion line-by-line */
1868 while(pos < i_len) {
1869 if (psz_text[pos] != '\n')
1871 p_fribidi_string[pos] = psz_text[pos];
1872 pp_char_styles_new[pos] = pp_char_styles[pos];
1873 p_new_positions[pos] = pos;
1878 while(pos < i_len) {
1879 if (psz_text[pos] == '\n')
1883 if (pos > start_pos)
1885 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1886 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1887 pos - start_pos, &base_dir,
1888 (FriBidiChar*)p_fribidi_string + start_pos,
1889 p_new_positions + start_pos,
1891 p_levels + start_pos );
1892 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1894 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1895 p_old_positions[ j - start_pos ] ];
1896 p_new_positions[ j ] += start_pos;
1900 free( p_old_positions );
1901 free( pp_char_styles );
1902 pp_char_styles = pp_char_styles_new;
1903 psz_text = p_fribidi_string;
1904 p_fribidi_string[ i_len ] = 0;
1907 /* Work out the karaoke */
1908 if( pi_karaoke_bar )
1910 int64_t i_last_duration = 0;
1911 int64_t i_duration = 0;
1912 int64_t i_start_pos = 0;
1913 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1915 for( k = 0; k< i_k_runs; k++ )
1917 double fraction = 0.0;
1919 i_duration += pi_k_durations[ k ];
1921 if( i_duration < i_elapsed )
1923 /* Completely finished this run-length -
1924 * let it render normally */
1928 else if( i_elapsed < i_last_duration )
1930 /* Haven't got up to this segment yet -
1931 * render it completely in karaoke BG mode */
1937 /* Partway through this run */
1939 fraction = (double)(i_elapsed - i_last_duration) /
1940 (double)pi_k_durations[ k ];
1942 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1944 double shade = pi_k_run_lengths[ k ] * fraction;
1946 if( p_new_positions )
1947 j = p_new_positions[ i_start_pos + i ];
1949 j = i_start_pos + i;
1951 if( i < (uint32_t)shade )
1952 pi_karaoke_bar[ j ] = 0xff;
1953 else if( (double)i > shade )
1954 pi_karaoke_bar[ j ] = 0x00;
1957 shade -= (int)shade;
1958 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1959 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1963 i_last_duration = i_duration;
1964 i_start_pos += pi_k_run_lengths[ k ];
1968 free( p_new_positions );
1970 FT_Vector tmp_result;
1972 line_desc_t *p_line = NULL;
1973 line_desc_t *p_prev = NULL;
1979 p_result->x = p_result->y = 0;
1980 tmp_result.x = tmp_result.y = 0;
1983 for( k = 0; k <= (uint32_t) i_len; k++ )
1985 if( ( k == (uint32_t) i_len ) ||
1987 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1989 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1991 /* End of the current style run */
1992 FT_Face p_face = NULL;
1995 /* Look for a match amongst our attachments first */
1996 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2000 char *psz_fontfile = NULL;
2002 psz_fontfile = FontConfig_Select( NULL,
2003 p_style->psz_fontname,
2007 if( psz_fontfile && ! *psz_fontfile )
2009 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2010 " so using default font", p_style->psz_fontname,
2011 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2012 (p_style->b_bold ? "(Bold)" :
2013 (p_style->b_italic ? "(Italic)" : ""))) );
2014 free( psz_fontfile );
2015 psz_fontfile = NULL;
2020 if( FT_New_Face( p_sys->p_library,
2021 psz_fontfile, i_idx, &p_face ) )
2023 free( psz_fontfile );
2024 free( pp_char_styles );
2025 #if defined(HAVE_FRIBIDI)
2028 free( pi_karaoke_bar );
2029 return VLC_EGENERIC;
2031 free( psz_fontfile );
2035 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2037 /* We've loaded a font face which is unhelpful for actually
2038 * rendering text - fallback to the default one.
2040 FT_Done_Face( p_face );
2044 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2045 ft_encoding_unicode ) ||
2046 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2047 p_style->i_font_size ) )
2049 if( p_face ) FT_Done_Face( p_face );
2050 free( pp_char_styles );
2051 #if defined(HAVE_FRIBIDI)
2054 free( pi_karaoke_bar );
2055 return VLC_EGENERIC;
2057 p_sys->i_use_kerning =
2058 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2061 uint32_t *psz_unicode = (uint32_t *)
2062 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2065 if( p_face ) FT_Done_Face( p_face );
2066 free( pp_char_styles );
2067 free( psz_unicode );
2068 #if defined(HAVE_FRIBIDI)
2071 free( pi_karaoke_bar );
2074 memcpy( psz_unicode, psz_text + i_prev,
2075 sizeof( uint32_t ) * ( k - i_prev ) );
2076 psz_unicode[ k - i_prev ] = 0;
2077 while( *psz_unicode )
2081 if( !(p_line = NewLine( i_len - i_prev)) )
2083 if( p_face ) FT_Done_Face( p_face );
2084 free( pp_char_styles );
2085 free( psz_unicode );
2086 #if defined(HAVE_FRIBIDI)
2089 free( pi_karaoke_bar );
2092 /* New Color mode only works in YUVA rendering mode --
2093 * (RGB mode has palette constraints on it). We therefore
2094 * need to populate the legacy colour fields also.
2096 p_line->b_new_color_mode = true;
2097 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2098 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2099 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2100 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2101 p_line->p_next = NULL;
2103 i_pen_y += tmp_result.y;
2107 if( p_prev ) p_prev->p_next = p_line;
2108 else *pp_lines = p_line;
2111 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2112 p_style->i_font_color, p_style->b_underline,
2114 p_style->i_karaoke_bg_color,
2115 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2116 &tmp_result ) != VLC_SUCCESS )
2118 if( p_face ) FT_Done_Face( p_face );
2119 free( pp_char_styles );
2120 free( psz_unicode );
2121 #if defined(HAVE_FRIBIDI)
2124 free( pi_karaoke_bar );
2125 return VLC_EGENERIC;
2130 p_result->x = __MAX( p_result->x, tmp_result.x );
2131 p_result->y += tmp_result.y;
2136 if( *psz_unicode == '\n')
2140 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2142 *c_ptr = *(c_ptr+1);
2147 free( psz_unicode );
2148 if( p_face ) FT_Done_Face( p_face );
2152 free( pp_char_styles );
2153 #if defined(HAVE_FRIBIDI)
2158 p_result->x = __MAX( p_result->x, tmp_result.x );
2159 p_result->y += tmp_result.y;
2162 if( pi_karaoke_bar )
2165 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2167 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2169 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2173 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2175 /* 100% BG colour will render faster if we
2176 * instead make it 100% FG colour, so leave
2177 * the ratio alone and copy the value across
2179 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2183 if( pi_karaoke_bar[ i ] & 0x80 )
2185 /* Swap Left and Right sides over for Right aligned
2186 * language text (eg. Arabic, Hebrew)
2188 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2190 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2191 p_line->p_bg_rgb[ k ] = i_tmp;
2193 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2196 /* Jump over the '\n' at the line-end */
2199 free( pi_karaoke_bar );
2205 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2206 subpicture_region_t *p_region_in )
2208 int rv = VLC_SUCCESS;
2209 stream_t *p_sub = NULL;
2210 xml_reader_t *p_xml_reader = NULL;
2212 if( !p_region_in || !p_region_in->psz_html )
2213 return VLC_EGENERIC;
2215 /* Reset the default fontsize in case screen metrics have changed */
2216 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2218 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2219 (uint8_t *) p_region_in->psz_html,
2220 strlen( p_region_in->psz_html ),
2224 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2225 if( p_filter->p_sys->p_xml )
2227 bool b_karaoke = false;
2229 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2232 /* Look for Root Node */
2233 if( xml_ReaderRead( p_xml_reader ) == 1 )
2235 char *psz_node = xml_ReaderName( p_xml_reader );
2237 if( !strcasecmp( "karaoke", psz_node ) )
2239 /* We're going to have to render the text a number
2240 * of times to show the progress marker on the text.
2242 var_SetBool( p_filter, "text-rerender", true );
2245 else if( !strcasecmp( "text", psz_node ) )
2251 /* Only text and karaoke tags are supported */
2252 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2253 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2254 p_xml_reader = NULL;
2266 uint32_t i_runs = 0;
2267 uint32_t i_k_runs = 0;
2268 uint32_t *pi_run_lengths = NULL;
2269 uint32_t *pi_k_run_lengths = NULL;
2270 uint32_t *pi_k_durations = NULL;
2271 ft_style_t **pp_styles = NULL;
2273 line_desc_t *p_lines = NULL;
2275 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2276 sizeof( uint32_t ) );
2281 rv = ProcessNodes( p_filter, p_xml_reader,
2282 p_region_in->p_style, psz_text, &i_len,
2283 &i_runs, &pi_run_lengths, &pp_styles,
2285 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2288 p_region_out->i_x = p_region_in->i_x;
2289 p_region_out->i_y = p_region_in->i_y;
2291 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2293 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2294 pi_run_lengths, pp_styles, &p_lines, &result,
2295 b_karaoke, i_k_runs, pi_k_run_lengths,
2299 for( k=0; k<i_runs; k++)
2300 DeleteStyle( pp_styles[k] );
2302 free( pi_run_lengths );
2305 /* Don't attempt to render text that couldn't be layed out
2308 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2310 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2312 Render( p_filter, p_region_out, p_lines,
2313 result.x, result.y );
2317 RenderYUVA( p_filter, p_region_out, p_lines,
2318 result.x, result.y );
2322 FreeLines( p_lines );
2324 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2327 stream_Delete( p_sub );
2333 static char* FontConfig_Select( FcConfig* priv, const char* family,
2334 bool b_bold, bool b_italic, int *i_idx )
2337 FcPattern *pat, *p_pat;
2341 pat = FcPatternCreate();
2342 if (!pat) return NULL;
2344 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2345 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2346 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2347 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2349 FcDefaultSubstitute( pat );
2351 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2353 FcPatternDestroy( pat );
2357 p_pat = FcFontMatch( priv, pat, &result );
2358 FcPatternDestroy( pat );
2359 if( !p_pat ) return NULL;
2361 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2362 || ( val_b != FcTrue ) )
2364 FcPatternDestroy( p_pat );
2367 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2372 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2374 FcPatternDestroy( p_pat );
2379 if( strcasecmp((const char*)val_s, family ) != 0 )
2380 msg_Warn( p_filter, "fontconfig: selected font family is not"
2381 "the requested one: '%s' != '%s'\n",
2382 (const char*)val_s, family );
2385 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2387 FcPatternDestroy( p_pat );
2391 FcPatternDestroy( p_pat );
2392 return strdup( (const char*)val_s );
2396 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2397 uint32_t **psz_text_out, uint32_t *pi_runs,
2398 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2399 ft_style_t *p_style )
2401 VLC_UNUSED(p_filter);
2402 VLC_UNUSED(psz_text_in);
2403 VLC_UNUSED(psz_text_out);
2404 VLC_UNUSED(pi_runs);
2405 VLC_UNUSED(ppi_run_lengths);
2406 VLC_UNUSED(ppp_styles);
2407 VLC_UNUSED(p_style);
2410 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2411 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2412 bool b_uline, bool b_through )
2415 VLC_UNUSED(p_fonts);
2417 VLC_UNUSED(b_italic);
2418 VLC_UNUSED(b_uline);
2419 VLC_UNUSED(b_through);
2424 static void FreeLine( line_desc_t *p_line )
2427 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2429 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2431 free( p_line->pp_glyphs );
2432 free( p_line->p_glyph_pos );
2433 free( p_line->p_fg_rgb );
2434 free( p_line->p_bg_rgb );
2435 free( p_line->p_fg_bg_ratio );
2436 free( p_line->pi_underline_offset );
2437 free( p_line->pi_underline_thickness );
2441 static void FreeLines( line_desc_t *p_lines )
2443 line_desc_t *p_line, *p_next;
2445 if( !p_lines ) return;
2447 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2449 p_next = p_line->p_next;
2454 static line_desc_t *NewLine( int i_count )
2456 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2458 if( !p_line ) return NULL;
2459 p_line->i_height = 0;
2460 p_line->i_width = 0;
2461 p_line->p_next = NULL;
2463 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2464 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2465 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2466 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2467 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2468 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2469 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2470 if( ( p_line->pp_glyphs == NULL ) ||
2471 ( p_line->p_glyph_pos == NULL ) ||
2472 ( p_line->p_fg_rgb == NULL ) ||
2473 ( p_line->p_bg_rgb == NULL ) ||
2474 ( p_line->p_fg_bg_ratio == NULL ) ||
2475 ( p_line->pi_underline_offset == NULL ) ||
2476 ( p_line->pi_underline_thickness == NULL ) )
2478 free( p_line->pi_underline_thickness );
2479 free( p_line->pi_underline_offset );
2480 free( p_line->p_fg_rgb );
2481 free( p_line->p_bg_rgb );
2482 free( p_line->p_fg_bg_ratio );
2483 free( p_line->p_glyph_pos );
2484 free( p_line->pp_glyphs );
2488 p_line->pp_glyphs[0] = NULL;
2489 p_line->b_new_color_mode = false;
2494 static int GetFontSize( filter_t *p_filter )
2496 filter_sys_t *p_sys = p_filter->p_sys;
2500 if( p_sys->i_default_font_size )
2502 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2503 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2505 i_size = p_sys->i_default_font_size;
2509 var_Get( p_filter, "freetype-rel-fontsize", &val );
2512 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2513 p_filter->p_sys->i_display_height =
2514 p_filter->fmt_out.video.i_height;
2519 msg_Warn( p_filter, "invalid fontsize, using 12" );
2520 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2521 i_size = 12 * val.i_int / 1000;
2528 static int SetFontSize( filter_t *p_filter, int i_size )
2530 filter_sys_t *p_sys = p_filter->p_sys;
2534 i_size = GetFontSize( p_filter );
2536 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2539 p_sys->i_font_size = i_size;
2541 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2543 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2544 return VLC_EGENERIC;
2550 static void YUVFromRGB( uint32_t i_argb,
2551 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2553 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2554 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2555 int i_blue = ( i_argb & 0x000000ff );
2557 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2558 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2559 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2560 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2561 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2562 -585 * i_blue + 4096 + 1048576) >> 13, 240);