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>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
57 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
58 #define FC_DEFAULT_FONT "Lucida Grande"
59 #elif defined( SYS_BEOS )
60 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
61 #define FC_DEFAULT_FONT "Swiss"
62 #elif defined( WIN32 )
63 #define DEFAULT_FONT "" /* Default font found at run-time */
64 #define FC_DEFAULT_FONT "Arial"
66 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
67 #define FC_DEFAULT_FONT "Serif Bold"
70 #if defined(HAVE_FRIBIDI)
71 #include <fribidi/fribidi.h>
74 #ifdef HAVE_FONTCONFIG
75 #include <fontconfig/fontconfig.h>
77 #define DEFAULT_FONT FC_DEFAULT_FONT
82 /*****************************************************************************
84 *****************************************************************************/
85 static int Create ( vlc_object_t * );
86 static void Destroy( vlc_object_t * );
88 #define FONT_TEXT N_("Font")
90 #ifdef HAVE_FONTCONFIG
91 #define FONT_LONGTEXT N_("Font family for the font you want to use")
93 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
96 #define FONTSIZE_TEXT N_("Font size in pixels")
97 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
98 "that will be rendered on the video. " \
99 "If set to something different than 0 this option will override the " \
100 "relative font size." )
101 #define OPACITY_TEXT N_("Opacity")
102 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
103 "text that will be rendered on the video. 0 = transparent, " \
104 "255 = totally opaque. " )
105 #define COLOR_TEXT N_("Text default color")
106 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
107 "the video. This must be an hexadecimal (like HTML colors). The first two "\
108 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
109 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
110 #define FONTSIZER_TEXT N_("Relative font size")
111 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
112 "fonts that will be rendered on the video. If absolute font size is set, "\
113 "relative size will be overriden." )
115 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
116 static const char *const ppsz_sizes_text[] = {
117 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
118 #define YUVP_TEXT N_("Use YUVP renderer")
119 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
120 "This option is only needed if you want to encode into DVB subtitles" )
121 #define EFFECT_TEXT N_("Font Effect")
122 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
123 "text to improve its readability." )
125 #define EFFECT_BACKGROUND 1
126 #define EFFECT_OUTLINE 2
127 #define EFFECT_OUTLINE_FAT 3
129 static int const pi_effects[] = { 1, 2, 3 };
130 static const char *const ppsz_effects_text[] = {
131 N_("Background"),N_("Outline"), N_("Fat Outline") };
132 static const int pi_color_values[] = {
133 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
134 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
135 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
137 static const char *const ppsz_color_descriptions[] = {
138 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
139 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
140 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
143 set_shortname( N_("Text renderer"))
144 set_description( N_("Freetype2 font renderer") )
145 set_category( CAT_VIDEO )
146 set_subcategory( SUBCAT_VIDEO_SUBPIC )
148 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
151 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
152 FONTSIZE_LONGTEXT, true )
154 /* opacity valid on 0..255, with default 255 = fully opaque */
155 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
156 OPACITY_TEXT, OPACITY_LONGTEXT, true )
158 /* hook to the color values list, with default 0x00ffffff = white */
159 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
160 COLOR_LONGTEXT, false )
161 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
163 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
164 FONTSIZER_LONGTEXT, false )
165 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
166 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
167 EFFECT_LONGTEXT, false )
168 change_integer_list( pi_effects, ppsz_effects_text, NULL )
170 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
171 YUVP_LONGTEXT, true )
172 set_capability( "text renderer", 100 )
173 add_shortcut( "text" )
174 set_callbacks( Create, Destroy )
179 /*****************************************************************************
181 *****************************************************************************/
183 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
184 static int RenderText( filter_t *, subpicture_region_t *,
185 subpicture_region_t * );
186 #ifdef HAVE_FONTCONFIG
187 static int RenderHtml( filter_t *, subpicture_region_t *,
188 subpicture_region_t * );
189 static char *FontConfig_Select( FcConfig *, const char *,
194 static int LoadFontsFromAttachments( filter_t *p_filter );
196 static int GetFontSize( filter_t *p_filter );
197 static int SetFontSize( filter_t *, int );
198 static void YUVFromRGB( uint32_t i_argb,
199 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
201 typedef struct line_desc_t line_desc_t;
204 /** NULL-terminated list of glyphs making the string */
205 FT_BitmapGlyph *pp_glyphs;
206 /** list of relative positions for the glyphs */
207 FT_Vector *p_glyph_pos;
208 /** list of RGB information for styled text
209 * -- if the rendering mode supports it (RenderYUVA) and
210 * b_new_color_mode is set, then it becomes possible to
211 * have multicoloured text within the subtitles. */
214 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
215 bool b_new_color_mode;
216 /** underline information -- only supplied if text should be underlined */
217 uint16_t *pi_underline_offset;
218 uint16_t *pi_underline_thickness;
222 int i_red, i_green, i_blue;
227 static line_desc_t *NewLine( int );
232 uint32_t i_font_color; /* ARGB */
233 uint32_t i_karaoke_bg_color; /* ARGB */
240 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
241 static void FreeLines( line_desc_t * );
242 static void FreeLine( line_desc_t * );
244 /*****************************************************************************
245 * filter_sys_t: freetype local data
246 *****************************************************************************
247 * This structure is part of the video output thread descriptor.
248 * It describes the freetype specific properties of an output thread.
249 *****************************************************************************/
252 FT_Library p_library; /* handle to library */
253 FT_Face p_face; /* handle to face object */
255 uint8_t i_font_opacity;
260 int i_default_font_size;
261 int i_display_height;
262 #ifdef HAVE_FONTCONFIG
263 char* psz_fontfamily;
267 input_attachment_t **pp_font_attachments;
268 int i_font_attachments;
272 #define UCHAR uint32_t
273 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
274 #define TR_FONT_STYLE_PTR ft_style_t *
276 #include "text_renderer.h"
278 /*****************************************************************************
279 * Create: allocates osd-text video thread output method
280 *****************************************************************************
281 * This function allocates and initializes a Clone vout method.
282 *****************************************************************************/
283 static int Create( vlc_object_t *p_this )
285 filter_t *p_filter = (filter_t *)p_this;
287 char *psz_fontfile=NULL;
288 char *psz_fontfamily=NULL;
289 int i_error,fontindex;
291 #ifdef HAVE_FONTCONFIG
292 FcPattern *fontpattern = NULL, *fontmatch = NULL;
293 /* Initialise result to Match, as fontconfig doesnt
294 * really set this other than some error-cases */
295 FcResult fontresult = FcResultMatch;
299 /* Allocate structure */
300 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
303 #ifdef HAVE_FONTCONFIG
304 p_sys->psz_fontfamily = NULL;
308 p_sys->p_library = 0;
309 p_sys->i_font_size = 0;
310 p_sys->i_display_height = 0;
312 var_Create( p_filter, "freetype-rel-fontsize",
313 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
315 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
316 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
317 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
318 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
319 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
320 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
321 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
324 if( !psz_fontfamily || !*psz_fontfamily )
326 #ifdef HAVE_FONTCONFIG
327 free( psz_fontfamily);
328 psz_fontfamily=strdup( DEFAULT_FONT );
330 free( psz_fontfamily );
331 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
332 if( !psz_fontfamily )
335 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
336 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
338 strcpy( psz_fontfamily, DEFAULT_FONT );
340 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
344 #ifdef HAVE_FONTCONFIG
345 /* Lets find some fontfile from freetype-font variable family */
347 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
351 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
352 _("Building font cache"),
353 _("Please wait while your font cache is rebuilt.\n"
354 "This should take less than few minutes."), NULL );
356 path = (char *)malloc( PATH_MAX + 1 );
357 /* Fontconfig doesnt seem to know where windows fonts are with
358 * current contribs. So just tell default windows font directory
359 * is the place to search fonts
361 GetWindowsDirectory( path, PATH_MAX + 1 );
362 strcat( path, "\\fonts" );
364 dialog_ProgressSet( p_dialog, NULL, 0.4 );
366 FcConfigAppFontAddDir( NULL , path );
371 dialog_ProgressSet( p_dialog, NULL, 0.5 );
375 msg_Dbg( p_filter, "Building font database.");
377 FcConfigBuildFonts( NULL );
380 msg_Dbg( p_filter, "Finished building font database." );
381 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
383 fontpattern = FcPatternCreate();
387 msg_Err( p_filter, "Creating fontpattern failed");
393 dialog_ProgressSet( p_dialog, NULL, 0.7 );
395 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
396 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
397 free( psz_fontsize );
399 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
401 msg_Err( p_filter, "FontSubstitute failed");
404 FcDefaultSubstitute( fontpattern );
408 dialog_ProgressSet( p_dialog, NULL, 0.8 );
410 /* testing fontresult here doesn't do any good really, but maybe it will
411 * in future as fontconfig code doesn't set it in all cases and just
412 * returns NULL or doesn't set to to Match on all Match cases.*/
413 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
414 if( !fontmatch || fontresult == FcResultNoMatch )
416 msg_Err( p_filter, "Fontmatching failed");
420 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
421 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
424 msg_Err( p_filter, "Failed to get fontfile");
428 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
429 p_sys->psz_fontfamily = strdup( psz_fontfamily );
433 dialog_ProgressSet( p_dialog, NULL, 1.0 );
434 dialog_ProgressDestroy( p_dialog );
440 #ifdef HAVE_FONTCONFIG
441 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
442 psz_fontfile = psz_fontfamily;
447 i_error = FT_Init_FreeType( &p_sys->p_library );
450 msg_Err( p_filter, "couldn't initialize freetype" );
454 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
455 fontindex, &p_sys->p_face );
457 if( i_error == FT_Err_Unknown_File_Format )
459 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
464 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
468 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
471 msg_Err( p_filter, "font has no unicode translation table" );
475 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
477 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
480 p_sys->pp_font_attachments = NULL;
481 p_sys->i_font_attachments = 0;
483 p_filter->pf_render_text = RenderText;
484 #ifdef HAVE_FONTCONFIG
485 p_filter->pf_render_html = RenderHtml;
486 FcPatternDestroy( fontmatch );
487 FcPatternDestroy( fontpattern );
489 p_filter->pf_render_html = NULL;
492 free( psz_fontfamily );
493 LoadFontsFromAttachments( p_filter );
498 #ifdef HAVE_FONTCONFIG
499 if( fontmatch ) FcPatternDestroy( fontmatch );
500 if( fontpattern ) FcPatternDestroy( fontpattern );
502 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
503 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
504 free( psz_fontfamily );
509 /*****************************************************************************
510 * Destroy: destroy Clone video thread output method
511 *****************************************************************************
512 * Clean up all data and library connections
513 *****************************************************************************/
514 static void Destroy( vlc_object_t *p_this )
516 filter_t *p_filter = (filter_t *)p_this;
517 filter_sys_t *p_sys = p_filter->p_sys;
519 if( p_sys->pp_font_attachments )
523 for( k = 0; k < p_sys->i_font_attachments; k++ )
524 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
526 free( p_sys->pp_font_attachments );
529 #ifdef HAVE_FONTCONFIG
530 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
531 free( p_sys->psz_fontfamily );
534 /* FcFini asserts calling the subfunction FcCacheFini()
535 * even if no other library functions have been made since FcInit(),
536 * so don't call it. */
538 FT_Done_Face( p_sys->p_face );
539 FT_Done_FreeType( p_sys->p_library );
543 /*****************************************************************************
544 * Make any TTF/OTF fonts present in the attachments of the media file
545 * and store them for later use by the FreeType Engine
546 *****************************************************************************/
547 static int LoadFontsFromAttachments( filter_t *p_filter )
549 filter_sys_t *p_sys = p_filter->p_sys;
550 input_thread_t *p_input;
551 input_attachment_t **pp_attachments;
552 int i_attachments_cnt;
554 int rv = VLC_SUCCESS;
556 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
560 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
562 vlc_object_release(p_input);
566 p_sys->i_font_attachments = 0;
567 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
568 if(! p_sys->pp_font_attachments )
571 for( k = 0; k < i_attachments_cnt; k++ )
573 input_attachment_t *p_attach = pp_attachments[k];
575 if( p_sys->pp_font_attachments )
577 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
578 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
579 ( p_attach->i_data > 0 ) &&
580 ( p_attach->p_data != NULL ) )
582 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
586 vlc_input_attachment_Delete( p_attach );
591 vlc_input_attachment_Delete( p_attach );
594 free( pp_attachments );
596 vlc_object_release(p_input);
601 /*****************************************************************************
602 * Render: place string in picture
603 *****************************************************************************
604 * This function merges the previously rendered freetype glyphs into a picture
605 *****************************************************************************/
606 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
607 line_desc_t *p_line, int i_width, int i_height )
609 VLC_UNUSED(p_filter);
610 static const uint8_t pi_gamma[16] =
611 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
612 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
616 int i, x, y, i_pitch;
617 uint8_t i_y; /* YUV values, derived from incoming RGB */
620 /* Create a new subpicture region */
621 memset( &fmt, 0, sizeof(video_format_t) );
622 fmt.i_chroma = VLC_CODEC_YUVP;
624 fmt.i_width = fmt.i_visible_width = i_width + 4;
625 fmt.i_height = fmt.i_visible_height = i_height + 4;
626 if( p_region->fmt.i_visible_width > 0 )
627 fmt.i_visible_width = p_region->fmt.i_visible_width;
628 if( p_region->fmt.i_visible_height > 0 )
629 fmt.i_visible_height = p_region->fmt.i_visible_height;
630 fmt.i_x_offset = fmt.i_y_offset = 0;
632 assert( !p_region->p_picture );
633 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
634 if( !p_region->p_picture )
638 /* Calculate text color components */
639 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
640 25 * p_line->i_blue + 128) >> 8) + 16;
641 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
642 112 * p_line->i_blue + 128) >> 8) + 128;
643 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
644 18 * p_line->i_blue + 128) >> 8) + 128;
647 fmt.p_palette->i_entries = 16;
648 for( i = 0; i < 8; i++ )
650 fmt.p_palette->palette[i][0] = 0;
651 fmt.p_palette->palette[i][1] = 0x80;
652 fmt.p_palette->palette[i][2] = 0x80;
653 fmt.p_palette->palette[i][3] = pi_gamma[i];
654 fmt.p_palette->palette[i][3] =
655 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
657 for( i = 8; i < fmt.p_palette->i_entries; i++ )
659 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
660 fmt.p_palette->palette[i][1] = i_u;
661 fmt.p_palette->palette[i][2] = i_v;
662 fmt.p_palette->palette[i][3] = pi_gamma[i];
663 fmt.p_palette->palette[i][3] =
664 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
667 p_dst = p_region->p_picture->Y_PIXELS;
668 i_pitch = p_region->p_picture->Y_PITCH;
670 /* Initialize the region pixels */
671 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
673 for( ; p_line != NULL; p_line = p_line->p_next )
675 int i_glyph_tmax = 0;
676 int i_bitmap_offset, i_offset, i_align_offset = 0;
677 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
679 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
680 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
683 if( p_line->i_width < i_width )
685 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
687 i_align_offset = i_width - p_line->i_width;
689 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
691 i_align_offset = ( i_width - p_line->i_width ) / 2;
695 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
697 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
699 i_offset = ( p_line->p_glyph_pos[ i ].y +
700 i_glyph_tmax - p_glyph->top + 2 ) *
701 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
704 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
706 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
708 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
710 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
717 /* Outlining (find something better than nearest neighbour filtering ?) */
720 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
721 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
722 uint8_t left, current;
724 for( y = 1; y < (int)fmt.i_height - 1; y++ )
726 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
727 p_dst += p_region->p_picture->Y_PITCH;
730 for( x = 1; x < (int)fmt.i_width - 1; x++ )
733 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
734 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;
738 memset( p_top, 0, fmt.i_width );
744 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
745 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
746 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
747 int i_glyph_tmax, int i_align_offset,
748 uint8_t i_y, uint8_t i_u, uint8_t i_v,
749 subpicture_region_t *p_region)
753 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
755 p_dst_y = p_region->p_picture->Y_PIXELS;
756 p_dst_u = p_region->p_picture->U_PIXELS;
757 p_dst_v = p_region->p_picture->V_PIXELS;
758 p_dst_a = p_region->p_picture->A_PIXELS;
759 i_pitch = p_region->p_picture->A_PITCH;
761 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
762 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
764 for( y = 0; y < i_line_thickness; y++ )
766 int i_extra = p_this_glyph->bitmap.width;
770 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
771 (p_this_glyph_pos->x + p_this_glyph->left);
773 for( x = 0; x < i_extra; x++ )
777 /* break the underline around the tails of any glyphs which cross it */
778 for( z = x - i_line_thickness;
779 z < x + i_line_thickness && b_ok;
782 if( p_next_glyph && ( z >= i_extra ) )
784 int i_row = i_line_offset + p_next_glyph->top + y;
786 if( ( p_next_glyph->bitmap.rows > i_row ) &&
787 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
792 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
794 int i_row = i_line_offset + p_this_glyph->top + y;
796 if( ( p_this_glyph->bitmap.rows > i_row ) &&
797 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
806 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
807 p_dst_u[i_offset+x] = i_u;
808 p_dst_v[i_offset+x] = i_v;
809 p_dst_a[i_offset+x] = 255;
816 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
818 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
819 int i_pitch = p_region->p_picture->A_PITCH;
822 for( ; p_line != NULL; p_line = p_line->p_next )
824 int i_glyph_tmax=0, i = 0;
825 int i_bitmap_offset, i_offset, i_align_offset = 0;
826 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
828 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
829 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
832 if( p_line->i_width < i_width )
834 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
836 i_align_offset = i_width - p_line->i_width;
838 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
840 i_align_offset = ( i_width - p_line->i_width ) / 2;
844 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
846 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
848 i_offset = ( p_line->p_glyph_pos[ i ].y +
849 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
850 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
851 i_align_offset +xoffset;
853 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
855 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
857 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
858 if( p_dst[i_offset+x] <
859 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
861 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
870 /*****************************************************************************
871 * Render: place string in picture
872 *****************************************************************************
873 * This function merges the previously rendered freetype glyphs into a picture
874 *****************************************************************************/
875 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
876 line_desc_t *p_line, int i_width, int i_height )
878 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
880 int i, x, y, i_pitch, i_alpha;
881 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
883 if( i_width == 0 || i_height == 0 )
886 /* Create a new subpicture region */
887 memset( &fmt, 0, sizeof(video_format_t) );
888 fmt.i_chroma = VLC_CODEC_YUVA;
890 fmt.i_width = fmt.i_visible_width = i_width + 6;
891 fmt.i_height = fmt.i_visible_height = i_height + 6;
892 if( p_region->fmt.i_visible_width > 0 )
893 fmt.i_visible_width = p_region->fmt.i_visible_width;
894 if( p_region->fmt.i_visible_height > 0 )
895 fmt.i_visible_height = p_region->fmt.i_visible_height;
896 fmt.i_x_offset = fmt.i_y_offset = 0;
898 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
899 if( !p_region->p_picture )
903 /* Calculate text color components */
904 YUVFromRGB( (p_line->i_red << 16) |
905 (p_line->i_green << 8) |
908 i_alpha = p_line->i_alpha;
910 p_dst_y = p_region->p_picture->Y_PIXELS;
911 p_dst_u = p_region->p_picture->U_PIXELS;
912 p_dst_v = p_region->p_picture->V_PIXELS;
913 p_dst_a = p_region->p_picture->A_PIXELS;
914 i_pitch = p_region->p_picture->A_PITCH;
916 /* Initialize the region pixels */
917 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
919 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
920 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
921 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
922 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
926 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
927 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
928 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
929 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
931 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
932 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
934 DrawBlack( p_line, i_width, p_region, 0, 0);
935 DrawBlack( p_line, i_width, p_region, -1, 0);
936 DrawBlack( p_line, i_width, p_region, 0, -1);
937 DrawBlack( p_line, i_width, p_region, 1, 0);
938 DrawBlack( p_line, i_width, p_region, 0, 1);
941 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
943 DrawBlack( p_line, i_width, p_region, -1, -1);
944 DrawBlack( p_line, i_width, p_region, -1, 1);
945 DrawBlack( p_line, i_width, p_region, 1, -1);
946 DrawBlack( p_line, i_width, p_region, 1, 1);
948 DrawBlack( p_line, i_width, p_region, -2, 0);
949 DrawBlack( p_line, i_width, p_region, 0, -2);
950 DrawBlack( p_line, i_width, p_region, 2, 0);
951 DrawBlack( p_line, i_width, p_region, 0, 2);
953 DrawBlack( p_line, i_width, p_region, -2, -2);
954 DrawBlack( p_line, i_width, p_region, -2, 2);
955 DrawBlack( p_line, i_width, p_region, 2, -2);
956 DrawBlack( p_line, i_width, p_region, 2, 2);
958 DrawBlack( p_line, i_width, p_region, -3, 0);
959 DrawBlack( p_line, i_width, p_region, 0, -3);
960 DrawBlack( p_line, i_width, p_region, 3, 0);
961 DrawBlack( p_line, i_width, p_region, 0, 3);
964 for( ; p_line != NULL; p_line = p_line->p_next )
966 int i_glyph_tmax = 0;
967 int i_bitmap_offset, i_offset, i_align_offset = 0;
968 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
970 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
971 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
974 if( p_line->i_width < i_width )
976 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
978 i_align_offset = i_width - p_line->i_width;
980 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
982 i_align_offset = ( i_width - p_line->i_width ) / 2;
986 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
988 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
990 i_offset = ( p_line->p_glyph_pos[ i ].y +
991 i_glyph_tmax - p_glyph->top + 3 ) *
992 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
995 if( p_line->b_new_color_mode )
997 /* Every glyph can (and in fact must) have its own color */
998 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1001 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1003 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1005 uint8_t i_y_local = i_y;
1006 uint8_t i_u_local = i_u;
1007 uint8_t i_v_local = i_v;
1009 if( p_line->p_fg_bg_ratio != 0x00 )
1011 int i_split = p_glyph->bitmap.width *
1012 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1016 YUVFromRGB( p_line->p_bg_rgb[ i ],
1017 &i_y_local, &i_u_local, &i_v_local );
1021 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1023 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1024 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1026 p_dst_u[i_offset+x] = i_u;
1027 p_dst_v[i_offset+x] = i_v;
1029 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1030 p_dst_a[i_offset+x] = 0xff;
1033 i_offset += i_pitch;
1036 if( p_line->pi_underline_thickness[ i ] )
1038 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1039 p_line->pi_underline_offset[ i ],
1040 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1041 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1042 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1043 i_glyph_tmax, i_align_offset,
1050 /* Apply the alpha setting */
1051 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1052 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1058 * This function renders a text subpicture region into another one.
1059 * It also calculates the size needed for this string, and renders the
1060 * needed glyphs into memory. It is used as pf_add_string callback in
1061 * the vout method by this module
1063 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1064 subpicture_region_t *p_region_in )
1066 filter_sys_t *p_sys = p_filter->p_sys;
1067 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1068 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1069 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1070 int i_string_length;
1072 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1073 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1083 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1084 psz_string = p_region_in->psz_text;
1085 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1087 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1088 i_scale = val.i_int;
1090 if( p_region_in->p_style )
1092 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1093 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1094 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1098 i_font_color = p_sys->i_font_color;
1099 i_font_alpha = 255 - p_sys->i_font_opacity;
1100 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1103 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1104 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1105 SetFontSize( p_filter, i_font_size );
1107 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1108 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1109 i_blue = i_font_color & 0x000000FF;
1111 result.x = result.y = 0;
1112 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1114 psz_unicode = psz_unicode_orig =
1115 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1116 if( psz_unicode == NULL )
1118 #if defined(WORDS_BIGENDIAN)
1119 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1121 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1123 if( iconv_handle == (vlc_iconv_t)-1 )
1125 msg_Warn( p_filter, "unable to do conversion" );
1131 const char *p_in_buffer = psz_string;
1132 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1133 i_in_bytes = strlen( psz_string );
1134 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1135 i_out_bytes_left = i_out_bytes;
1136 p_out_buffer = (char *)psz_unicode;
1137 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1139 &p_out_buffer, &i_out_bytes_left );
1141 vlc_iconv_close( iconv_handle );
1145 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1146 "bytes left %u", (unsigned)i_in_bytes );
1149 *(uint32_t*)p_out_buffer = 0;
1150 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1153 #if defined(HAVE_FRIBIDI)
1155 uint32_t *p_fribidi_string;
1156 int32_t start_pos, pos = 0;
1158 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1159 if( !p_fribidi_string )
1162 /* Do bidi conversion line-by-line */
1163 while( pos < i_string_length )
1165 while( pos < i_string_length )
1167 i_char = psz_unicode[pos];
1168 if (i_char != '\r' && i_char != '\n')
1170 p_fribidi_string[pos] = i_char;
1174 while( pos < i_string_length )
1176 i_char = psz_unicode[pos];
1177 if (i_char == '\r' || i_char == '\n')
1181 if (pos > start_pos)
1183 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1184 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1187 (FriBidiChar*)p_fribidi_string + start_pos,
1192 free( psz_unicode_orig );
1193 psz_unicode = psz_unicode_orig = p_fribidi_string;
1194 p_fribidi_string[ i_string_length ] = 0;
1198 /* Calculate relative glyph positions and a bounding box for the
1200 if( !(p_line = NewLine( strlen( psz_string ))) )
1203 i_pen_x = i_pen_y = 0;
1205 psz_line_start = psz_unicode;
1207 #define face p_sys->p_face
1208 #define glyph face->glyph
1210 while( *psz_unicode )
1212 i_char = *psz_unicode++;
1213 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1218 if( i_char == '\n' )
1220 psz_line_start = psz_unicode;
1221 if( !(p_next = NewLine( strlen( psz_string ))) )
1223 p_line->p_next = p_next;
1224 p_line->i_width = line.xMax;
1225 p_line->i_height = face->size->metrics.height >> 6;
1226 p_line->pp_glyphs[ i ] = NULL;
1227 p_line->i_alpha = i_font_alpha;
1228 p_line->i_red = i_red;
1229 p_line->i_green = i_green;
1230 p_line->i_blue = i_blue;
1233 result.x = __MAX( result.x, line.xMax );
1234 result.y += face->size->metrics.height >> 6;
1237 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1238 i_pen_y += face->size->metrics.height >> 6;
1240 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1245 i_glyph_index = FT_Get_Char_Index( face, i_char );
1246 if( p_sys->i_use_kerning && i_glyph_index
1250 FT_Get_Kerning( face, i_previous, i_glyph_index,
1251 ft_kerning_default, &delta );
1252 i_pen_x += delta.x >> 6;
1255 p_line->p_glyph_pos[ i ].x = i_pen_x;
1256 p_line->p_glyph_pos[ i ].y = i_pen_y;
1257 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1260 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1264 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1267 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1271 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1272 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1275 FT_Done_Glyph( tmp_glyph );
1278 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1281 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1282 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1283 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1285 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1286 p_line->pp_glyphs[ i ] = NULL;
1288 p_line = NewLine( strlen( psz_string ));
1289 if( p_prev ) p_prev->p_next = p_line;
1290 else p_lines = p_line;
1292 uint32_t *psz_unicode_saved = psz_unicode;
1293 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1297 if( psz_unicode == psz_line_start )
1298 { /* try harder to break that line */
1299 psz_unicode = psz_unicode_saved;
1300 while( psz_unicode > psz_line_start &&
1301 *psz_unicode != '_' && *psz_unicode != '/' &&
1302 *psz_unicode != '\\' && *psz_unicode != '.' )
1307 if( psz_unicode == psz_line_start )
1309 msg_Warn( p_filter, "unbreakable string" );
1314 *psz_unicode = '\n';
1316 psz_unicode = psz_line_start;
1319 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1322 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1323 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1325 i_previous = i_glyph_index;
1326 i_pen_x += glyph->advance.x >> 6;
1330 p_line->i_width = line.xMax;
1331 p_line->i_height = face->size->metrics.height >> 6;
1332 p_line->pp_glyphs[ i ] = NULL;
1333 p_line->i_alpha = i_font_alpha;
1334 p_line->i_red = i_red;
1335 p_line->i_green = i_green;
1336 p_line->i_blue = i_blue;
1337 result.x = __MAX( result.x, line.xMax );
1338 result.y += line.yMax - line.yMin;
1343 p_region_out->i_x = p_region_in->i_x;
1344 p_region_out->i_y = p_region_in->i_y;
1346 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1347 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1349 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1351 free( psz_unicode_orig );
1352 FreeLines( p_lines );
1356 free( psz_unicode_orig );
1357 FreeLines( p_lines );
1358 return VLC_EGENERIC;
1361 #ifdef HAVE_FONTCONFIG
1362 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1363 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1364 bool b_italic, bool b_uline )
1366 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1370 p_style->i_font_size = i_font_size;
1371 p_style->i_font_color = i_font_color;
1372 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1373 p_style->b_italic = b_italic;
1374 p_style->b_bold = b_bold;
1375 p_style->b_underline = b_uline;
1377 p_style->psz_fontname = strdup( psz_fontname );
1382 static void DeleteStyle( ft_style_t *p_style )
1386 free( p_style->psz_fontname );
1391 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1398 if(( s1->i_font_size == s2->i_font_size ) &&
1399 ( s1->i_font_color == s2->i_font_color ) &&
1400 ( s1->b_italic == s2->b_italic ) &&
1401 ( s1->b_bold == s2->b_bold ) &&
1402 ( s1->b_underline == s2->b_underline ) &&
1403 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1410 static void IconvText( filter_t *p_filter, const char *psz_string,
1411 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1413 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1415 /* If memory hasn't been allocated for our output string, allocate it here
1416 * - the calling function must now be responsible for freeing it.
1418 if( !*ppsz_unicode )
1419 *ppsz_unicode = (uint32_t *)
1420 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1422 /* We don't need to handle a NULL pointer in *ppsz_unicode
1423 * if we are instead testing for a non NULL value like we are here */
1427 #if defined(WORDS_BIGENDIAN)
1428 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1430 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1432 if( iconv_handle != (vlc_iconv_t)-1 )
1434 char *p_in_buffer, *p_out_buffer;
1435 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1436 i_in_bytes = strlen( psz_string );
1437 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1438 i_out_bytes_left = i_out_bytes;
1439 p_in_buffer = (char *) psz_string;
1440 p_out_buffer = (char *) *ppsz_unicode;
1441 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1442 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1444 vlc_iconv_close( iconv_handle );
1448 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1449 "bytes left %u", (unsigned)i_in_bytes );
1453 *(uint32_t*)p_out_buffer = 0;
1455 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1460 msg_Warn( p_filter, "unable to do conversion" );
1465 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1466 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1469 ft_style_t *p_style = NULL;
1471 char *psz_fontname = NULL;
1472 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1473 uint32_t i_karaoke_bg_color = i_font_color;
1474 int i_font_size = p_sys->i_font_size;
1476 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1477 &i_font_color, &i_karaoke_bg_color ))
1479 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1480 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1485 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1486 bool b_uline, int i_karaoke_bgcolor,
1487 line_desc_t *p_line, uint32_t *psz_unicode,
1488 int *pi_pen_x, int i_pen_y, int *pi_start,
1489 FT_Vector *p_result )
1494 bool b_first_on_line = true;
1497 int i_pen_x_start = *pi_pen_x;
1499 uint32_t *psz_unicode_start = psz_unicode;
1501 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1503 /* Account for part of line already in position */
1504 for( i=0; i<*pi_start; i++ )
1508 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1509 ft_glyph_bbox_pixels, &glyph_size );
1511 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1512 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1513 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1514 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1520 b_first_on_line = false;
1522 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1528 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1529 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1533 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1534 ft_kerning_default, &delta );
1535 *pi_pen_x += delta.x >> 6;
1537 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1538 p_line->p_glyph_pos[ i ].y = i_pen_y;
1540 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1544 "unable to render text FT_Load_Glyph returned %d", i_error );
1545 p_line->pp_glyphs[ i ] = NULL;
1546 return VLC_EGENERIC;
1548 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1552 "unable to render text FT_Get_Glyph returned %d", i_error );
1553 p_line->pp_glyphs[ i ] = NULL;
1554 return VLC_EGENERIC;
1556 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1557 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1560 FT_Done_Glyph( tmp_glyph );
1565 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1566 p_face->size->metrics.y_scale));
1567 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1568 p_face->size->metrics.y_scale));
1570 p_line->pi_underline_offset[ i ] =
1571 ( aOffset < 0 ) ? -aOffset : aOffset;
1572 p_line->pi_underline_thickness[ i ] =
1573 ( aSize < 0 ) ? -aSize : aSize;
1575 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1576 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1577 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1578 p_line->p_fg_bg_ratio[ i ] = 0x00;
1580 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1581 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1582 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1584 for( ; i >= *pi_start; i-- )
1585 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1588 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1592 if( psz_unicode == psz_unicode_start )
1594 if( b_first_on_line )
1596 msg_Warn( p_filter, "unbreakable string" );
1597 p_line->pp_glyphs[ i ] = NULL;
1598 return VLC_EGENERIC;
1600 *pi_pen_x = i_pen_x_start;
1602 p_line->i_width = line.xMax;
1603 p_line->i_height = __MAX( p_line->i_height,
1604 p_face->size->metrics.height >> 6 );
1605 p_line->pp_glyphs[ i ] = NULL;
1607 p_result->x = __MAX( p_result->x, line.xMax );
1608 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1609 i_yMax - i_yMin ) );
1614 *psz_unicode = '\n';
1616 psz_unicode = psz_unicode_start;
1617 *pi_pen_x = i_pen_x_start;
1625 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1626 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1628 i_previous = i_glyph_index;
1629 *pi_pen_x += p_face->glyph->advance.x >> 6;
1632 p_line->i_width = line.xMax;
1633 p_line->i_height = __MAX( p_line->i_height,
1634 p_face->size->metrics.height >> 6 );
1635 p_line->pp_glyphs[ i ] = NULL;
1637 p_result->x = __MAX( p_result->x, line.xMax );
1638 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1639 line.yMax - line.yMin ) );
1643 /* Get rid of any text processed - if necessary repositioning
1644 * at the start of a new line of text
1648 *psz_unicode_start = '\0';
1650 else if( psz_unicode > psz_unicode_start )
1652 for( i=0; psz_unicode[ i ]; i++ )
1653 psz_unicode_start[ i ] = psz_unicode[ i ];
1654 psz_unicode_start[ i ] = '\0';
1660 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1661 uint32_t **psz_text_out, uint32_t *pi_runs,
1662 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1663 ft_style_t *p_style )
1665 uint32_t i_string_length = 0;
1667 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1668 *psz_text_out += i_string_length;
1670 if( ppp_styles && ppi_run_lengths )
1676 *ppp_styles = (ft_style_t **)
1677 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1679 else if( *pi_runs == 1 )
1681 *ppp_styles = (ft_style_t **)
1682 malloc( *pi_runs * sizeof( ft_style_t * ) );
1685 /* We have just malloc'ed this memory successfully -
1686 * *pi_runs HAS to be within the memory area of *ppp_styles */
1689 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1693 if( *ppi_run_lengths )
1695 *ppi_run_lengths = (uint32_t *)
1696 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1698 else if( *pi_runs == 1 )
1700 *ppi_run_lengths = (uint32_t *)
1701 malloc( *pi_runs * sizeof( uint32_t ) );
1704 /* same remarks here */
1705 if( *ppi_run_lengths )
1707 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1710 /* If we couldn't use the p_style argument due to memory allocation
1711 * problems above, release it here.
1713 if( p_style ) DeleteStyle( p_style );
1716 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1720 for( k=0; k < p_sys->i_font_attachments; k++ )
1722 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1724 FT_Face p_face = NULL;
1726 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1734 bool match = !strcasecmp( p_face->family_name,
1735 p_style->psz_fontname );
1737 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1738 match = match && p_style->b_bold;
1740 match = match && !p_style->b_bold;
1742 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1743 match = match && p_style->b_italic;
1745 match = match && !p_style->b_italic;
1753 FT_Done_Face( p_face );
1758 return VLC_EGENERIC;
1761 static int ProcessLines( filter_t *p_filter,
1766 uint32_t *pi_run_lengths,
1767 ft_style_t **pp_styles,
1768 line_desc_t **pp_lines,
1770 FT_Vector *p_result,
1774 uint32_t *pi_k_run_lengths,
1775 uint32_t *pi_k_durations )
1777 filter_sys_t *p_sys = p_filter->p_sys;
1778 ft_style_t **pp_char_styles;
1779 int *p_new_positions = NULL;
1780 int8_t *p_levels = NULL;
1781 uint8_t *pi_karaoke_bar = NULL;
1785 /* Assign each character in the text string its style explicitly, so that
1786 * after the characters have been shuffled around by Fribidi, we can re-apply
1787 * the styles, and to simplify the calculation of runs within a line.
1789 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1790 if( !pp_char_styles )
1795 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1796 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1797 * we just won't be able to display the progress bar; at least we'll
1803 for( j = 0; j < i_runs; j++ )
1804 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1805 pp_char_styles[ i++ ] = pp_styles[ j ];
1807 #if defined(HAVE_FRIBIDI)
1809 ft_style_t **pp_char_styles_new;
1810 int *p_old_positions;
1811 uint32_t *p_fribidi_string;
1812 int start_pos, pos = 0;
1814 pp_char_styles_new = (ft_style_t **)
1815 malloc( i_len * sizeof( ft_style_t * ));
1817 p_fribidi_string = (uint32_t *)
1818 malloc( (i_len + 1) * sizeof(uint32_t) );
1819 p_old_positions = (int *)
1820 malloc( (i_len + 1) * sizeof( int ) );
1821 p_new_positions = (int *)
1822 malloc( (i_len + 1) * sizeof( int ) );
1823 p_levels = (int8_t *)
1824 malloc( (i_len + 1) * sizeof( int8_t ) );
1826 if( ! pp_char_styles_new ||
1827 ! p_fribidi_string ||
1828 ! p_old_positions ||
1829 ! p_new_positions ||
1833 free( p_old_positions );
1834 free( p_new_positions );
1835 free( p_fribidi_string );
1836 free( pp_char_styles_new );
1837 free( pi_karaoke_bar );
1839 free( pp_char_styles );
1843 /* Do bidi conversion line-by-line */
1846 while(pos < i_len) {
1847 if (psz_text[pos] != '\n')
1849 p_fribidi_string[pos] = psz_text[pos];
1850 pp_char_styles_new[pos] = pp_char_styles[pos];
1851 p_new_positions[pos] = pos;
1856 while(pos < i_len) {
1857 if (psz_text[pos] == '\n')
1861 if (pos > start_pos)
1863 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1864 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1865 pos - start_pos, &base_dir,
1866 (FriBidiChar*)p_fribidi_string + start_pos,
1867 p_new_positions + start_pos,
1869 p_levels + start_pos );
1870 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1872 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1873 p_old_positions[ j - start_pos ] ];
1874 p_new_positions[ j ] += start_pos;
1878 free( p_old_positions );
1879 free( pp_char_styles );
1880 pp_char_styles = pp_char_styles_new;
1881 psz_text = p_fribidi_string;
1882 p_fribidi_string[ i_len ] = 0;
1885 /* Work out the karaoke */
1886 if( pi_karaoke_bar )
1888 int64_t i_last_duration = 0;
1889 int64_t i_duration = 0;
1890 int64_t i_start_pos = 0;
1891 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1893 for( k = 0; k< i_k_runs; k++ )
1895 double fraction = 0.0;
1897 i_duration += pi_k_durations[ k ];
1899 if( i_duration < i_elapsed )
1901 /* Completely finished this run-length -
1902 * let it render normally */
1906 else if( i_elapsed < i_last_duration )
1908 /* Haven't got up to this segment yet -
1909 * render it completely in karaoke BG mode */
1915 /* Partway through this run */
1917 fraction = (double)(i_elapsed - i_last_duration) /
1918 (double)pi_k_durations[ k ];
1920 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1922 double shade = pi_k_run_lengths[ k ] * fraction;
1924 if( p_new_positions )
1925 j = p_new_positions[ i_start_pos + i ];
1927 j = i_start_pos + i;
1929 if( i < (uint32_t)shade )
1930 pi_karaoke_bar[ j ] = 0xff;
1931 else if( (double)i > shade )
1932 pi_karaoke_bar[ j ] = 0x00;
1935 shade -= (int)shade;
1936 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1937 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1941 i_last_duration = i_duration;
1942 i_start_pos += pi_k_run_lengths[ k ];
1946 free( p_new_positions );
1948 FT_Vector tmp_result;
1950 line_desc_t *p_line = NULL;
1951 line_desc_t *p_prev = NULL;
1957 p_result->x = p_result->y = 0;
1958 tmp_result.x = tmp_result.y = 0;
1961 for( k = 0; k <= (uint32_t) i_len; k++ )
1963 if( ( k == (uint32_t) i_len ) ||
1965 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1967 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1969 /* End of the current style run */
1970 FT_Face p_face = NULL;
1973 /* Look for a match amongst our attachments first */
1974 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1978 char *psz_fontfile = NULL;
1980 psz_fontfile = FontConfig_Select( NULL,
1981 p_style->psz_fontname,
1985 if( psz_fontfile && ! *psz_fontfile )
1987 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1988 " so using default font", p_style->psz_fontname,
1989 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1990 (p_style->b_bold ? "(Bold)" :
1991 (p_style->b_italic ? "(Italic)" : ""))) );
1992 free( psz_fontfile );
1993 psz_fontfile = NULL;
1998 if( FT_New_Face( p_sys->p_library,
1999 psz_fontfile, i_idx, &p_face ) )
2001 free( psz_fontfile );
2002 free( pp_char_styles );
2003 #if defined(HAVE_FRIBIDI)
2006 free( pi_karaoke_bar );
2007 return VLC_EGENERIC;
2009 free( psz_fontfile );
2013 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2015 /* We've loaded a font face which is unhelpful for actually
2016 * rendering text - fallback to the default one.
2018 FT_Done_Face( p_face );
2022 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2023 ft_encoding_unicode ) ||
2024 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2025 p_style->i_font_size ) )
2027 if( p_face ) FT_Done_Face( p_face );
2028 free( pp_char_styles );
2029 #if defined(HAVE_FRIBIDI)
2032 free( pi_karaoke_bar );
2033 return VLC_EGENERIC;
2035 p_sys->i_use_kerning =
2036 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2039 uint32_t *psz_unicode = (uint32_t *)
2040 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2043 if( p_face ) FT_Done_Face( p_face );
2044 free( pp_char_styles );
2045 free( psz_unicode );
2046 #if defined(HAVE_FRIBIDI)
2049 free( pi_karaoke_bar );
2052 memcpy( psz_unicode, psz_text + i_prev,
2053 sizeof( uint32_t ) * ( k - i_prev ) );
2054 psz_unicode[ k - i_prev ] = 0;
2055 while( *psz_unicode )
2059 if( !(p_line = NewLine( i_len - i_prev)) )
2061 if( p_face ) FT_Done_Face( p_face );
2062 free( pp_char_styles );
2063 free( psz_unicode );
2064 #if defined(HAVE_FRIBIDI)
2067 free( pi_karaoke_bar );
2070 /* New Color mode only works in YUVA rendering mode --
2071 * (RGB mode has palette constraints on it). We therefore
2072 * need to populate the legacy colour fields also.
2074 p_line->b_new_color_mode = true;
2075 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2076 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2077 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2078 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2079 p_line->p_next = NULL;
2081 i_pen_y += tmp_result.y;
2085 if( p_prev ) p_prev->p_next = p_line;
2086 else *pp_lines = p_line;
2089 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2090 p_style->i_font_color, p_style->b_underline,
2091 p_style->i_karaoke_bg_color,
2092 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2093 &tmp_result ) != VLC_SUCCESS )
2095 if( p_face ) FT_Done_Face( p_face );
2096 free( pp_char_styles );
2097 free( psz_unicode );
2098 #if defined(HAVE_FRIBIDI)
2101 free( pi_karaoke_bar );
2102 return VLC_EGENERIC;
2107 p_result->x = __MAX( p_result->x, tmp_result.x );
2108 p_result->y += tmp_result.y;
2113 if( *psz_unicode == '\n')
2117 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2119 *c_ptr = *(c_ptr+1);
2124 free( psz_unicode );
2125 if( p_face ) FT_Done_Face( p_face );
2129 free( pp_char_styles );
2130 #if defined(HAVE_FRIBIDI)
2135 p_result->x = __MAX( p_result->x, tmp_result.x );
2136 p_result->y += tmp_result.y;
2139 if( pi_karaoke_bar )
2142 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2144 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2146 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2150 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2152 /* 100% BG colour will render faster if we
2153 * instead make it 100% FG colour, so leave
2154 * the ratio alone and copy the value across
2156 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2160 if( pi_karaoke_bar[ i ] & 0x80 )
2162 /* Swap Left and Right sides over for Right aligned
2163 * language text (eg. Arabic, Hebrew)
2165 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2167 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2168 p_line->p_bg_rgb[ k ] = i_tmp;
2170 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2173 /* Jump over the '\n' at the line-end */
2176 free( pi_karaoke_bar );
2182 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2183 subpicture_region_t *p_region_in )
2185 int rv = VLC_SUCCESS;
2186 stream_t *p_sub = NULL;
2187 xml_reader_t *p_xml_reader = NULL;
2189 if( !p_region_in || !p_region_in->psz_html )
2190 return VLC_EGENERIC;
2192 /* Reset the default fontsize in case screen metrics have changed */
2193 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2195 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2196 (uint8_t *) p_region_in->psz_html,
2197 strlen( p_region_in->psz_html ),
2201 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2202 if( p_filter->p_sys->p_xml )
2204 bool b_karaoke = false;
2206 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2209 /* Look for Root Node */
2210 if( xml_ReaderRead( p_xml_reader ) == 1 )
2212 char *psz_node = xml_ReaderName( p_xml_reader );
2214 if( !strcasecmp( "karaoke", psz_node ) )
2216 /* We're going to have to render the text a number
2217 * of times to show the progress marker on the text.
2219 var_SetBool( p_filter, "text-rerender", true );
2222 else if( !strcasecmp( "text", psz_node ) )
2228 /* Only text and karaoke tags are supported */
2229 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2230 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2231 p_xml_reader = NULL;
2243 uint32_t i_runs = 0;
2244 uint32_t i_k_runs = 0;
2245 uint32_t *pi_run_lengths = NULL;
2246 uint32_t *pi_k_run_lengths = NULL;
2247 uint32_t *pi_k_durations = NULL;
2248 ft_style_t **pp_styles = NULL;
2250 line_desc_t *p_lines = NULL;
2252 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2253 sizeof( uint32_t ) );
2258 rv = ProcessNodes( p_filter, p_xml_reader,
2259 p_region_in->p_style, psz_text, &i_len,
2260 &i_runs, &pi_run_lengths, &pp_styles,
2262 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2265 p_region_out->i_x = p_region_in->i_x;
2266 p_region_out->i_y = p_region_in->i_y;
2268 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2270 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2271 pi_run_lengths, pp_styles, &p_lines, &result,
2272 b_karaoke, i_k_runs, pi_k_run_lengths,
2276 for( k=0; k<i_runs; k++)
2277 DeleteStyle( pp_styles[k] );
2279 free( pi_run_lengths );
2282 /* Don't attempt to render text that couldn't be layed out
2285 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2287 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2289 Render( p_filter, p_region_out, p_lines,
2290 result.x, result.y );
2294 RenderYUVA( p_filter, p_region_out, p_lines,
2295 result.x, result.y );
2299 FreeLines( p_lines );
2301 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2304 stream_Delete( p_sub );
2310 static char* FontConfig_Select( FcConfig* priv, const char* family,
2311 bool b_bold, bool b_italic, int *i_idx )
2314 FcPattern *pat, *p_pat;
2318 pat = FcPatternCreate();
2319 if (!pat) return NULL;
2321 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2322 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2323 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2324 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2326 FcDefaultSubstitute( pat );
2328 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2330 FcPatternDestroy( pat );
2334 p_pat = FcFontMatch( priv, pat, &result );
2335 FcPatternDestroy( pat );
2336 if( !p_pat ) return NULL;
2338 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2339 || ( val_b != FcTrue ) )
2341 FcPatternDestroy( p_pat );
2344 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2349 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2351 FcPatternDestroy( p_pat );
2356 if( strcasecmp((const char*)val_s, family ) != 0 )
2357 msg_Warn( p_filter, "fontconfig: selected font family is not"
2358 "the requested one: '%s' != '%s'\n",
2359 (const char*)val_s, family );
2362 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2364 FcPatternDestroy( p_pat );
2368 FcPatternDestroy( p_pat );
2369 return strdup( (const char*)val_s );
2373 static void FreeLine( line_desc_t *p_line )
2376 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2378 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2380 free( p_line->pp_glyphs );
2381 free( p_line->p_glyph_pos );
2382 free( p_line->p_fg_rgb );
2383 free( p_line->p_bg_rgb );
2384 free( p_line->p_fg_bg_ratio );
2385 free( p_line->pi_underline_offset );
2386 free( p_line->pi_underline_thickness );
2390 static void FreeLines( line_desc_t *p_lines )
2392 line_desc_t *p_line, *p_next;
2394 if( !p_lines ) return;
2396 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2398 p_next = p_line->p_next;
2403 static line_desc_t *NewLine( int i_count )
2405 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2407 if( !p_line ) return NULL;
2408 p_line->i_height = 0;
2409 p_line->i_width = 0;
2410 p_line->p_next = NULL;
2412 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2413 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2414 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2415 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2416 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2417 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2418 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2419 if( ( p_line->pp_glyphs == NULL ) ||
2420 ( p_line->p_glyph_pos == NULL ) ||
2421 ( p_line->p_fg_rgb == NULL ) ||
2422 ( p_line->p_bg_rgb == NULL ) ||
2423 ( p_line->p_fg_bg_ratio == NULL ) ||
2424 ( p_line->pi_underline_offset == NULL ) ||
2425 ( p_line->pi_underline_thickness == NULL ) )
2427 free( p_line->pi_underline_thickness );
2428 free( p_line->pi_underline_offset );
2429 free( p_line->p_fg_rgb );
2430 free( p_line->p_bg_rgb );
2431 free( p_line->p_fg_bg_ratio );
2432 free( p_line->p_glyph_pos );
2433 free( p_line->pp_glyphs );
2437 p_line->pp_glyphs[0] = NULL;
2438 p_line->b_new_color_mode = false;
2443 static int GetFontSize( filter_t *p_filter )
2445 filter_sys_t *p_sys = p_filter->p_sys;
2449 if( p_sys->i_default_font_size )
2451 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2452 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2454 i_size = p_sys->i_default_font_size;
2458 var_Get( p_filter, "freetype-rel-fontsize", &val );
2461 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2462 p_filter->p_sys->i_display_height =
2463 p_filter->fmt_out.video.i_height;
2468 msg_Warn( p_filter, "invalid fontsize, using 12" );
2469 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2470 i_size = 12 * val.i_int / 1000;
2477 static int SetFontSize( filter_t *p_filter, int i_size )
2479 filter_sys_t *p_sys = p_filter->p_sys;
2483 i_size = GetFontSize( p_filter );
2485 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2488 p_sys->i_font_size = i_size;
2490 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2492 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2493 return VLC_EGENERIC;
2499 static void YUVFromRGB( uint32_t i_argb,
2500 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2502 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2503 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2504 int i_blue = ( i_argb & 0x000000ff );
2506 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2507 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2508 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2509 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2510 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2511 -585 * i_blue + 4096 + 1048576) >> 13, 240);