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 #import <Carbon/Carbon.h>
58 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
59 #define FC_DEFAULT_FONT "Lucida Grande"
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"
67 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
68 #define FC_DEFAULT_FONT "Serif Bold"
71 #if defined(HAVE_FRIBIDI)
72 #include <fribidi/fribidi.h>
75 #ifdef HAVE_FONTCONFIG
76 #include <fontconfig/fontconfig.h>
78 #define DEFAULT_FONT FC_DEFAULT_FONT
83 /*****************************************************************************
85 *****************************************************************************/
86 static int Create ( vlc_object_t * );
87 static void Destroy( vlc_object_t * );
89 #define FONT_TEXT N_("Font")
91 #ifdef HAVE_FONTCONFIG
92 #define FONT_LONGTEXT N_("Font family for the font you want to use")
94 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
97 #define FONTSIZE_TEXT N_("Font size in pixels")
98 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
99 "that will be rendered on the video. " \
100 "If set to something different than 0 this option will override the " \
101 "relative font size." )
102 #define OPACITY_TEXT N_("Opacity")
103 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
104 "text that will be rendered on the video. 0 = transparent, " \
105 "255 = totally opaque. " )
106 #define COLOR_TEXT N_("Text default color")
107 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
108 "the video. This must be an hexadecimal (like HTML colors). The first two "\
109 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
110 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
111 #define FONTSIZER_TEXT N_("Relative font size")
112 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
113 "fonts that will be rendered on the video. If absolute font size is set, "\
114 "relative size will be overriden." )
116 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
117 static const char *const ppsz_sizes_text[] = {
118 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
119 #define YUVP_TEXT N_("Use YUVP renderer")
120 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
121 "This option is only needed if you want to encode into DVB subtitles" )
122 #define EFFECT_TEXT N_("Font Effect")
123 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
124 "text to improve its readability." )
126 #define EFFECT_BACKGROUND 1
127 #define EFFECT_OUTLINE 2
128 #define EFFECT_OUTLINE_FAT 3
130 static int const pi_effects[] = { 1, 2, 3 };
131 static const char *const ppsz_effects_text[] = {
132 N_("Background"),N_("Outline"), N_("Fat Outline") };
133 static const int pi_color_values[] = {
134 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
135 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
136 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
138 static const char *const ppsz_color_descriptions[] = {
139 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
140 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
141 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
144 set_shortname( N_("Text renderer"))
145 set_description( N_("Freetype2 font renderer") )
146 set_category( CAT_VIDEO )
147 set_subcategory( SUBCAT_VIDEO_SUBPIC )
149 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
152 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
153 FONTSIZE_LONGTEXT, true )
155 /* opacity valid on 0..255, with default 255 = fully opaque */
156 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
157 OPACITY_TEXT, OPACITY_LONGTEXT, true )
159 /* hook to the color values list, with default 0x00ffffff = white */
160 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
161 COLOR_LONGTEXT, false )
162 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
164 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
165 FONTSIZER_LONGTEXT, false )
166 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
167 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
168 EFFECT_LONGTEXT, false )
169 change_integer_list( pi_effects, ppsz_effects_text, NULL )
171 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
172 YUVP_LONGTEXT, true )
173 set_capability( "text renderer", 100 )
174 add_shortcut( "text" )
175 set_callbacks( Create, Destroy )
180 /*****************************************************************************
182 *****************************************************************************/
184 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
185 static int RenderText( filter_t *, subpicture_region_t *,
186 subpicture_region_t * );
187 #ifdef HAVE_FONTCONFIG
188 static int RenderHtml( filter_t *, subpicture_region_t *,
189 subpicture_region_t * );
190 static char *FontConfig_Select( FcConfig *, const char *,
195 static int LoadFontsFromAttachments( filter_t *p_filter );
197 static int GetFontSize( filter_t *p_filter );
198 static int SetFontSize( filter_t *, int );
199 static void YUVFromRGB( uint32_t i_argb,
200 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
202 typedef struct line_desc_t line_desc_t;
205 /** NULL-terminated list of glyphs making the string */
206 FT_BitmapGlyph *pp_glyphs;
207 /** list of relative positions for the glyphs */
208 FT_Vector *p_glyph_pos;
209 /** list of RGB information for styled text
210 * -- if the rendering mode supports it (RenderYUVA) and
211 * b_new_color_mode is set, then it becomes possible to
212 * have multicoloured text within the subtitles. */
215 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
216 bool b_new_color_mode;
217 /** underline information -- only supplied if text should be underlined */
218 uint16_t *pi_underline_offset;
219 uint16_t *pi_underline_thickness;
223 int i_red, i_green, i_blue;
228 static line_desc_t *NewLine( int );
233 uint32_t i_font_color; /* ARGB */
234 uint32_t i_karaoke_bg_color; /* ARGB */
241 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
242 static void FreeLines( line_desc_t * );
243 static void FreeLine( line_desc_t * );
245 /*****************************************************************************
246 * filter_sys_t: freetype local data
247 *****************************************************************************
248 * This structure is part of the video output thread descriptor.
249 * It describes the freetype specific properties of an output thread.
250 *****************************************************************************/
253 FT_Library p_library; /* handle to library */
254 FT_Face p_face; /* handle to face object */
256 uint8_t i_font_opacity;
261 int i_default_font_size;
262 int i_display_height;
263 #ifdef HAVE_FONTCONFIG
264 char* psz_fontfamily;
268 input_attachment_t **pp_font_attachments;
269 int i_font_attachments;
273 #define UCHAR uint32_t
274 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
275 #define TR_FONT_STYLE_PTR ft_style_t *
277 #include "text_renderer.h"
279 /*****************************************************************************
280 * Create: allocates osd-text video thread output method
281 *****************************************************************************
282 * This function allocates and initializes a Clone vout method.
283 *****************************************************************************/
284 static int Create( vlc_object_t *p_this )
286 filter_t *p_filter = (filter_t *)p_this;
288 char *psz_fontfile=NULL;
289 char *psz_fontfamily=NULL;
290 int i_error,fontindex;
292 #ifdef HAVE_FONTCONFIG
293 FcPattern *fontpattern = NULL, *fontmatch = NULL;
294 /* Initialise result to Match, as fontconfig doesnt
295 * really set this other than some error-cases */
296 FcResult fontresult = FcResultMatch;
300 /* Allocate structure */
301 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
304 #ifdef HAVE_FONTCONFIG
305 p_sys->psz_fontfamily = NULL;
309 p_sys->p_library = 0;
310 p_sys->i_font_size = 0;
311 p_sys->i_display_height = 0;
313 var_Create( p_filter, "freetype-rel-fontsize",
314 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
316 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
317 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
318 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
319 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
320 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
321 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
322 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
325 if( !psz_fontfamily || !*psz_fontfamily )
327 #ifdef HAVE_FONTCONFIG
328 free( psz_fontfamily);
329 psz_fontfamily=strdup( DEFAULT_FONT );
331 free( psz_fontfamily );
332 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
333 if( !psz_fontfamily )
336 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
337 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
340 if (Gestalt(gestaltSystemVersion, &MacVersion) == noErr)
342 if (MacVersion >= 0x1060)
344 strcpy( psz_fontfile, "/System/Library/Fonts/LucidaGrande.ttc" );
348 strcpy( psz_fontfile, DEFAULT_FONT );
352 strcpy( psz_fontfamily, DEFAULT_FONT );
354 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
358 #ifdef HAVE_FONTCONFIG
359 /* Lets find some fontfile from freetype-font variable family */
361 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
365 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
366 _("Building font cache"),
367 _("Please wait while your font cache is rebuilt.\n"
368 "This should take less than few minutes."), NULL );
370 path = (char *)malloc( PATH_MAX + 1 );
371 /* Fontconfig doesnt seem to know where windows fonts are with
372 * current contribs. So just tell default windows font directory
373 * is the place to search fonts
375 GetWindowsDirectory( path, PATH_MAX + 1 );
376 strcat( path, "\\fonts" );
378 dialog_ProgressSet( p_dialog, NULL, 0.4 );
380 FcConfigAppFontAddDir( NULL , path );
385 dialog_ProgressSet( p_dialog, NULL, 0.5 );
389 msg_Dbg( p_filter, "Building font database.");
391 FcConfigBuildFonts( NULL );
394 msg_Dbg( p_filter, "Finished building font database." );
395 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
397 fontpattern = FcPatternCreate();
401 msg_Err( p_filter, "Creating fontpattern failed");
407 dialog_ProgressSet( p_dialog, NULL, 0.7 );
409 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
410 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
411 free( psz_fontsize );
413 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
415 msg_Err( p_filter, "FontSubstitute failed");
418 FcDefaultSubstitute( fontpattern );
422 dialog_ProgressSet( p_dialog, NULL, 0.8 );
424 /* testing fontresult here doesn't do any good really, but maybe it will
425 * in future as fontconfig code doesn't set it in all cases and just
426 * returns NULL or doesn't set to to Match on all Match cases.*/
427 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
428 if( !fontmatch || fontresult == FcResultNoMatch )
430 msg_Err( p_filter, "Fontmatching failed");
434 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
435 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
438 msg_Err( p_filter, "Failed to get fontfile");
442 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
443 p_sys->psz_fontfamily = strdup( psz_fontfamily );
447 dialog_ProgressSet( p_dialog, NULL, 1.0 );
448 dialog_ProgressDestroy( p_dialog );
454 #ifdef HAVE_FONTCONFIG
455 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
456 psz_fontfile = psz_fontfamily;
461 i_error = FT_Init_FreeType( &p_sys->p_library );
464 msg_Err( p_filter, "couldn't initialize freetype" );
468 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
469 fontindex, &p_sys->p_face );
471 if( i_error == FT_Err_Unknown_File_Format )
473 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
478 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
482 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
485 msg_Err( p_filter, "font has no unicode translation table" );
489 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
491 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
494 p_sys->pp_font_attachments = NULL;
495 p_sys->i_font_attachments = 0;
497 p_filter->pf_render_text = RenderText;
498 #ifdef HAVE_FONTCONFIG
499 p_filter->pf_render_html = RenderHtml;
500 FcPatternDestroy( fontmatch );
501 FcPatternDestroy( fontpattern );
503 p_filter->pf_render_html = NULL;
506 free( psz_fontfamily );
507 LoadFontsFromAttachments( p_filter );
512 #ifdef HAVE_FONTCONFIG
513 if( fontmatch ) FcPatternDestroy( fontmatch );
514 if( fontpattern ) FcPatternDestroy( fontpattern );
516 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
517 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
518 free( psz_fontfamily );
523 /*****************************************************************************
524 * Destroy: destroy Clone video thread output method
525 *****************************************************************************
526 * Clean up all data and library connections
527 *****************************************************************************/
528 static void Destroy( vlc_object_t *p_this )
530 filter_t *p_filter = (filter_t *)p_this;
531 filter_sys_t *p_sys = p_filter->p_sys;
533 if( p_sys->pp_font_attachments )
537 for( k = 0; k < p_sys->i_font_attachments; k++ )
538 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
540 free( p_sys->pp_font_attachments );
543 #ifdef HAVE_FONTCONFIG
544 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
545 free( p_sys->psz_fontfamily );
548 /* FcFini asserts calling the subfunction FcCacheFini()
549 * even if no other library functions have been made since FcInit(),
550 * so don't call it. */
552 FT_Done_Face( p_sys->p_face );
553 FT_Done_FreeType( p_sys->p_library );
557 /*****************************************************************************
558 * Make any TTF/OTF fonts present in the attachments of the media file
559 * and store them for later use by the FreeType Engine
560 *****************************************************************************/
561 static int LoadFontsFromAttachments( filter_t *p_filter )
563 filter_sys_t *p_sys = p_filter->p_sys;
564 input_thread_t *p_input;
565 input_attachment_t **pp_attachments;
566 int i_attachments_cnt;
568 int rv = VLC_SUCCESS;
570 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
574 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
576 vlc_object_release(p_input);
580 p_sys->i_font_attachments = 0;
581 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
582 if(! p_sys->pp_font_attachments )
585 for( k = 0; k < i_attachments_cnt; k++ )
587 input_attachment_t *p_attach = pp_attachments[k];
589 if( p_sys->pp_font_attachments )
591 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
592 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
593 ( p_attach->i_data > 0 ) &&
594 ( p_attach->p_data != NULL ) )
596 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
600 vlc_input_attachment_Delete( p_attach );
605 vlc_input_attachment_Delete( p_attach );
608 free( pp_attachments );
610 vlc_object_release(p_input);
615 /*****************************************************************************
616 * Render: place string in picture
617 *****************************************************************************
618 * This function merges the previously rendered freetype glyphs into a picture
619 *****************************************************************************/
620 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
621 line_desc_t *p_line, int i_width, int i_height )
623 VLC_UNUSED(p_filter);
624 static const uint8_t pi_gamma[16] =
625 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
626 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
630 int i, x, y, i_pitch;
631 uint8_t i_y; /* YUV values, derived from incoming RGB */
634 /* Create a new subpicture region */
635 memset( &fmt, 0, sizeof(video_format_t) );
636 fmt.i_chroma = VLC_CODEC_YUVP;
638 fmt.i_width = fmt.i_visible_width = i_width + 4;
639 fmt.i_height = fmt.i_visible_height = i_height + 4;
640 if( p_region->fmt.i_visible_width > 0 )
641 fmt.i_visible_width = p_region->fmt.i_visible_width;
642 if( p_region->fmt.i_visible_height > 0 )
643 fmt.i_visible_height = p_region->fmt.i_visible_height;
644 fmt.i_x_offset = fmt.i_y_offset = 0;
646 assert( !p_region->p_picture );
647 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
648 if( !p_region->p_picture )
652 /* Calculate text color components */
653 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
654 25 * p_line->i_blue + 128) >> 8) + 16;
655 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
656 112 * p_line->i_blue + 128) >> 8) + 128;
657 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
658 18 * p_line->i_blue + 128) >> 8) + 128;
661 fmt.p_palette->i_entries = 16;
662 for( i = 0; i < 8; i++ )
664 fmt.p_palette->palette[i][0] = 0;
665 fmt.p_palette->palette[i][1] = 0x80;
666 fmt.p_palette->palette[i][2] = 0x80;
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;
671 for( i = 8; i < fmt.p_palette->i_entries; i++ )
673 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
674 fmt.p_palette->palette[i][1] = i_u;
675 fmt.p_palette->palette[i][2] = i_v;
676 fmt.p_palette->palette[i][3] = pi_gamma[i];
677 fmt.p_palette->palette[i][3] =
678 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
681 p_dst = p_region->p_picture->Y_PIXELS;
682 i_pitch = p_region->p_picture->Y_PITCH;
684 /* Initialize the region pixels */
685 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
687 for( ; p_line != NULL; p_line = p_line->p_next )
689 int i_glyph_tmax = 0;
690 int i_bitmap_offset, i_offset, i_align_offset = 0;
691 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
693 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
694 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
697 if( p_line->i_width < i_width )
699 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
701 i_align_offset = i_width - p_line->i_width;
703 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
705 i_align_offset = ( i_width - p_line->i_width ) / 2;
709 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
711 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
713 i_offset = ( p_line->p_glyph_pos[ i ].y +
714 i_glyph_tmax - p_glyph->top + 2 ) *
715 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
718 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
720 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
722 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
724 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
731 /* Outlining (find something better than nearest neighbour filtering ?) */
734 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
735 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
736 uint8_t left, current;
738 for( y = 1; y < (int)fmt.i_height - 1; y++ )
740 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
741 p_dst += p_region->p_picture->Y_PITCH;
744 for( x = 1; x < (int)fmt.i_width - 1; x++ )
747 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
748 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;
752 memset( p_top, 0, fmt.i_width );
758 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
759 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
760 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
761 int i_glyph_tmax, int i_align_offset,
762 uint8_t i_y, uint8_t i_u, uint8_t i_v,
763 subpicture_region_t *p_region)
767 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
769 p_dst_y = p_region->p_picture->Y_PIXELS;
770 p_dst_u = p_region->p_picture->U_PIXELS;
771 p_dst_v = p_region->p_picture->V_PIXELS;
772 p_dst_a = p_region->p_picture->A_PIXELS;
773 i_pitch = p_region->p_picture->A_PITCH;
775 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
776 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
778 for( y = 0; y < i_line_thickness; y++ )
780 int i_extra = p_this_glyph->bitmap.width;
784 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
785 (p_this_glyph_pos->x + p_this_glyph->left);
787 for( x = 0; x < i_extra; x++ )
791 /* break the underline around the tails of any glyphs which cross it */
792 for( z = x - i_line_thickness;
793 z < x + i_line_thickness && b_ok;
796 if( p_next_glyph && ( z >= i_extra ) )
798 int i_row = i_line_offset + p_next_glyph->top + y;
800 if( ( p_next_glyph->bitmap.rows > i_row ) &&
801 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
806 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
808 int i_row = i_line_offset + p_this_glyph->top + y;
810 if( ( p_this_glyph->bitmap.rows > i_row ) &&
811 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
820 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
821 p_dst_u[i_offset+x] = i_u;
822 p_dst_v[i_offset+x] = i_v;
823 p_dst_a[i_offset+x] = 255;
830 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
832 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
833 int i_pitch = p_region->p_picture->A_PITCH;
836 for( ; p_line != NULL; p_line = p_line->p_next )
838 int i_glyph_tmax=0, i = 0;
839 int i_bitmap_offset, i_offset, i_align_offset = 0;
840 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
842 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
843 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
846 if( p_line->i_width < i_width )
848 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
850 i_align_offset = i_width - p_line->i_width;
852 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
854 i_align_offset = ( i_width - p_line->i_width ) / 2;
858 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
860 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
862 i_offset = ( p_line->p_glyph_pos[ i ].y +
863 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
864 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
865 i_align_offset +xoffset;
867 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
869 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
871 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
872 if( p_dst[i_offset+x] <
873 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
875 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
884 /*****************************************************************************
885 * Render: place string in picture
886 *****************************************************************************
887 * This function merges the previously rendered freetype glyphs into a picture
888 *****************************************************************************/
889 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
890 line_desc_t *p_line, int i_width, int i_height )
892 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
894 int i, x, y, i_pitch, i_alpha;
895 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
897 if( i_width == 0 || i_height == 0 )
900 /* Create a new subpicture region */
901 memset( &fmt, 0, sizeof(video_format_t) );
902 fmt.i_chroma = VLC_CODEC_YUVA;
904 fmt.i_width = fmt.i_visible_width = i_width + 6;
905 fmt.i_height = fmt.i_visible_height = i_height + 6;
906 if( p_region->fmt.i_visible_width > 0 )
907 fmt.i_visible_width = p_region->fmt.i_visible_width;
908 if( p_region->fmt.i_visible_height > 0 )
909 fmt.i_visible_height = p_region->fmt.i_visible_height;
910 fmt.i_x_offset = fmt.i_y_offset = 0;
912 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
913 if( !p_region->p_picture )
917 /* Calculate text color components */
918 YUVFromRGB( (p_line->i_red << 16) |
919 (p_line->i_green << 8) |
922 i_alpha = p_line->i_alpha;
924 p_dst_y = p_region->p_picture->Y_PIXELS;
925 p_dst_u = p_region->p_picture->U_PIXELS;
926 p_dst_v = p_region->p_picture->V_PIXELS;
927 p_dst_a = p_region->p_picture->A_PIXELS;
928 i_pitch = p_region->p_picture->A_PITCH;
930 /* Initialize the region pixels */
931 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
933 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
934 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
935 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
936 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
940 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
941 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
942 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
943 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
945 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
946 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
948 DrawBlack( p_line, i_width, p_region, 0, 0);
949 DrawBlack( p_line, i_width, p_region, -1, 0);
950 DrawBlack( p_line, i_width, p_region, 0, -1);
951 DrawBlack( p_line, i_width, p_region, 1, 0);
952 DrawBlack( p_line, i_width, p_region, 0, 1);
955 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
957 DrawBlack( p_line, i_width, p_region, -1, -1);
958 DrawBlack( p_line, i_width, p_region, -1, 1);
959 DrawBlack( p_line, i_width, p_region, 1, -1);
960 DrawBlack( p_line, i_width, p_region, 1, 1);
962 DrawBlack( p_line, i_width, p_region, -2, 0);
963 DrawBlack( p_line, i_width, p_region, 0, -2);
964 DrawBlack( p_line, i_width, p_region, 2, 0);
965 DrawBlack( p_line, i_width, p_region, 0, 2);
967 DrawBlack( p_line, i_width, p_region, -2, -2);
968 DrawBlack( p_line, i_width, p_region, -2, 2);
969 DrawBlack( p_line, i_width, p_region, 2, -2);
970 DrawBlack( p_line, i_width, p_region, 2, 2);
972 DrawBlack( p_line, i_width, p_region, -3, 0);
973 DrawBlack( p_line, i_width, p_region, 0, -3);
974 DrawBlack( p_line, i_width, p_region, 3, 0);
975 DrawBlack( p_line, i_width, p_region, 0, 3);
978 for( ; p_line != NULL; p_line = p_line->p_next )
980 int i_glyph_tmax = 0;
981 int i_bitmap_offset, i_offset, i_align_offset = 0;
982 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
984 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
985 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
988 if( p_line->i_width < i_width )
990 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
992 i_align_offset = i_width - p_line->i_width;
994 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
996 i_align_offset = ( i_width - p_line->i_width ) / 2;
1000 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
1002 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
1004 i_offset = ( p_line->p_glyph_pos[ i ].y +
1005 i_glyph_tmax - p_glyph->top + 3 ) *
1006 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1009 if( p_line->b_new_color_mode )
1011 /* Every glyph can (and in fact must) have its own color */
1012 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1015 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1017 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1019 uint8_t i_y_local = i_y;
1020 uint8_t i_u_local = i_u;
1021 uint8_t i_v_local = i_v;
1023 if( p_line->p_fg_bg_ratio != 0x00 )
1025 int i_split = p_glyph->bitmap.width *
1026 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1030 YUVFromRGB( p_line->p_bg_rgb[ i ],
1031 &i_y_local, &i_u_local, &i_v_local );
1035 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1037 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1038 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1040 p_dst_u[i_offset+x] = i_u;
1041 p_dst_v[i_offset+x] = i_v;
1043 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1044 p_dst_a[i_offset+x] = 0xff;
1047 i_offset += i_pitch;
1050 if( p_line->pi_underline_thickness[ i ] )
1052 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1053 p_line->pi_underline_offset[ i ],
1054 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1055 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1056 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1057 i_glyph_tmax, i_align_offset,
1064 /* Apply the alpha setting */
1065 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1066 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1072 * This function renders a text subpicture region into another one.
1073 * It also calculates the size needed for this string, and renders the
1074 * needed glyphs into memory. It is used as pf_add_string callback in
1075 * the vout method by this module
1077 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1078 subpicture_region_t *p_region_in )
1080 filter_sys_t *p_sys = p_filter->p_sys;
1081 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1082 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1083 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1084 int i_string_length;
1086 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1087 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1097 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1098 psz_string = p_region_in->psz_text;
1099 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1101 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1102 i_scale = val.i_int;
1104 if( p_region_in->p_style )
1106 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1107 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1108 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1112 i_font_color = p_sys->i_font_color;
1113 i_font_alpha = 255 - p_sys->i_font_opacity;
1114 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1117 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1118 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1119 SetFontSize( p_filter, i_font_size );
1121 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1122 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1123 i_blue = i_font_color & 0x000000FF;
1125 result.x = result.y = 0;
1126 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1128 psz_unicode = psz_unicode_orig =
1129 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1130 if( psz_unicode == NULL )
1132 #if defined(WORDS_BIGENDIAN)
1133 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1135 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1137 if( iconv_handle == (vlc_iconv_t)-1 )
1139 msg_Warn( p_filter, "unable to do conversion" );
1145 const char *p_in_buffer = psz_string;
1146 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1147 i_in_bytes = strlen( psz_string );
1148 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1149 i_out_bytes_left = i_out_bytes;
1150 p_out_buffer = (char *)psz_unicode;
1151 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1153 &p_out_buffer, &i_out_bytes_left );
1155 vlc_iconv_close( iconv_handle );
1159 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1160 "bytes left %u", (unsigned)i_in_bytes );
1163 *(uint32_t*)p_out_buffer = 0;
1164 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1167 #if defined(HAVE_FRIBIDI)
1169 uint32_t *p_fribidi_string;
1170 int32_t start_pos, pos = 0;
1172 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1173 if( !p_fribidi_string )
1176 /* Do bidi conversion line-by-line */
1177 while( pos < i_string_length )
1179 while( pos < i_string_length )
1181 i_char = psz_unicode[pos];
1182 if (i_char != '\r' && i_char != '\n')
1184 p_fribidi_string[pos] = i_char;
1188 while( pos < i_string_length )
1190 i_char = psz_unicode[pos];
1191 if (i_char == '\r' || i_char == '\n')
1195 if (pos > start_pos)
1197 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1198 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1201 (FriBidiChar*)p_fribidi_string + start_pos,
1206 free( psz_unicode_orig );
1207 psz_unicode = psz_unicode_orig = p_fribidi_string;
1208 p_fribidi_string[ i_string_length ] = 0;
1212 /* Calculate relative glyph positions and a bounding box for the
1214 if( !(p_line = NewLine( strlen( psz_string ))) )
1217 i_pen_x = i_pen_y = 0;
1219 psz_line_start = psz_unicode;
1221 #define face p_sys->p_face
1222 #define glyph face->glyph
1224 while( *psz_unicode )
1226 i_char = *psz_unicode++;
1227 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1232 if( i_char == '\n' )
1234 psz_line_start = psz_unicode;
1235 if( !(p_next = NewLine( strlen( psz_string ))) )
1237 p_line->p_next = p_next;
1238 p_line->i_width = line.xMax;
1239 p_line->i_height = face->size->metrics.height >> 6;
1240 p_line->pp_glyphs[ i ] = NULL;
1241 p_line->i_alpha = i_font_alpha;
1242 p_line->i_red = i_red;
1243 p_line->i_green = i_green;
1244 p_line->i_blue = i_blue;
1247 result.x = __MAX( result.x, line.xMax );
1248 result.y += face->size->metrics.height >> 6;
1251 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1252 i_pen_y += face->size->metrics.height >> 6;
1254 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1259 i_glyph_index = FT_Get_Char_Index( face, i_char );
1260 if( p_sys->i_use_kerning && i_glyph_index
1264 FT_Get_Kerning( face, i_previous, i_glyph_index,
1265 ft_kerning_default, &delta );
1266 i_pen_x += delta.x >> 6;
1269 p_line->p_glyph_pos[ i ].x = i_pen_x;
1270 p_line->p_glyph_pos[ i ].y = i_pen_y;
1271 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1274 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1278 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1281 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1285 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1286 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1289 FT_Done_Glyph( tmp_glyph );
1292 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1295 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1296 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1297 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1299 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1300 p_line->pp_glyphs[ i ] = NULL;
1302 p_line = NewLine( strlen( psz_string ));
1303 if( p_prev ) p_prev->p_next = p_line;
1304 else p_lines = p_line;
1306 uint32_t *psz_unicode_saved = psz_unicode;
1307 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1311 if( psz_unicode == psz_line_start )
1312 { /* try harder to break that line */
1313 psz_unicode = psz_unicode_saved;
1314 while( psz_unicode > psz_line_start &&
1315 *psz_unicode != '_' && *psz_unicode != '/' &&
1316 *psz_unicode != '\\' && *psz_unicode != '.' )
1321 if( psz_unicode == psz_line_start )
1323 msg_Warn( p_filter, "unbreakable string" );
1328 *psz_unicode = '\n';
1330 psz_unicode = psz_line_start;
1333 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1336 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1337 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1339 i_previous = i_glyph_index;
1340 i_pen_x += glyph->advance.x >> 6;
1344 p_line->i_width = line.xMax;
1345 p_line->i_height = face->size->metrics.height >> 6;
1346 p_line->pp_glyphs[ i ] = NULL;
1347 p_line->i_alpha = i_font_alpha;
1348 p_line->i_red = i_red;
1349 p_line->i_green = i_green;
1350 p_line->i_blue = i_blue;
1351 result.x = __MAX( result.x, line.xMax );
1352 result.y += line.yMax - line.yMin;
1357 p_region_out->i_x = p_region_in->i_x;
1358 p_region_out->i_y = p_region_in->i_y;
1360 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1361 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1363 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1365 free( psz_unicode_orig );
1366 FreeLines( p_lines );
1370 free( psz_unicode_orig );
1371 FreeLines( p_lines );
1372 return VLC_EGENERIC;
1375 #ifdef HAVE_FONTCONFIG
1376 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1377 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1378 bool b_italic, bool b_uline )
1380 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1384 p_style->i_font_size = i_font_size;
1385 p_style->i_font_color = i_font_color;
1386 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1387 p_style->b_italic = b_italic;
1388 p_style->b_bold = b_bold;
1389 p_style->b_underline = b_uline;
1391 p_style->psz_fontname = strdup( psz_fontname );
1396 static void DeleteStyle( ft_style_t *p_style )
1400 free( p_style->psz_fontname );
1405 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1412 if(( s1->i_font_size == s2->i_font_size ) &&
1413 ( s1->i_font_color == s2->i_font_color ) &&
1414 ( s1->b_italic == s2->b_italic ) &&
1415 ( s1->b_bold == s2->b_bold ) &&
1416 ( s1->b_underline == s2->b_underline ) &&
1417 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1424 static void IconvText( filter_t *p_filter, const char *psz_string,
1425 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1427 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1429 /* If memory hasn't been allocated for our output string, allocate it here
1430 * - the calling function must now be responsible for freeing it.
1432 if( !*ppsz_unicode )
1433 *ppsz_unicode = (uint32_t *)
1434 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1436 /* We don't need to handle a NULL pointer in *ppsz_unicode
1437 * if we are instead testing for a non NULL value like we are here */
1441 #if defined(WORDS_BIGENDIAN)
1442 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1444 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1446 if( iconv_handle != (vlc_iconv_t)-1 )
1448 char *p_in_buffer, *p_out_buffer;
1449 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1450 i_in_bytes = strlen( psz_string );
1451 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1452 i_out_bytes_left = i_out_bytes;
1453 p_in_buffer = (char *) psz_string;
1454 p_out_buffer = (char *) *ppsz_unicode;
1455 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1456 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1458 vlc_iconv_close( iconv_handle );
1462 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1463 "bytes left %u", (unsigned)i_in_bytes );
1467 *(uint32_t*)p_out_buffer = 0;
1469 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1474 msg_Warn( p_filter, "unable to do conversion" );
1479 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1480 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1483 ft_style_t *p_style = NULL;
1485 char *psz_fontname = NULL;
1486 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1487 uint32_t i_karaoke_bg_color = i_font_color;
1488 int i_font_size = p_sys->i_font_size;
1490 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1491 &i_font_color, &i_karaoke_bg_color ))
1493 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1494 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1499 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1500 bool b_uline, int i_karaoke_bgcolor,
1501 line_desc_t *p_line, uint32_t *psz_unicode,
1502 int *pi_pen_x, int i_pen_y, int *pi_start,
1503 FT_Vector *p_result )
1508 bool b_first_on_line = true;
1511 int i_pen_x_start = *pi_pen_x;
1513 uint32_t *psz_unicode_start = psz_unicode;
1515 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1517 /* Account for part of line already in position */
1518 for( i=0; i<*pi_start; i++ )
1522 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1523 ft_glyph_bbox_pixels, &glyph_size );
1525 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1526 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1527 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1528 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1534 b_first_on_line = false;
1536 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1542 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1543 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1547 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1548 ft_kerning_default, &delta );
1549 *pi_pen_x += delta.x >> 6;
1551 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1552 p_line->p_glyph_pos[ i ].y = i_pen_y;
1554 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1558 "unable to render text FT_Load_Glyph returned %d", i_error );
1559 p_line->pp_glyphs[ i ] = NULL;
1560 return VLC_EGENERIC;
1562 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1566 "unable to render text FT_Get_Glyph returned %d", i_error );
1567 p_line->pp_glyphs[ i ] = NULL;
1568 return VLC_EGENERIC;
1570 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1571 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1574 FT_Done_Glyph( tmp_glyph );
1579 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1580 p_face->size->metrics.y_scale));
1581 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1582 p_face->size->metrics.y_scale));
1584 p_line->pi_underline_offset[ i ] =
1585 ( aOffset < 0 ) ? -aOffset : aOffset;
1586 p_line->pi_underline_thickness[ i ] =
1587 ( aSize < 0 ) ? -aSize : aSize;
1589 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1590 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1591 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1592 p_line->p_fg_bg_ratio[ i ] = 0x00;
1594 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1595 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1596 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1598 for( ; i >= *pi_start; i-- )
1599 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1602 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1606 if( psz_unicode == psz_unicode_start )
1608 if( b_first_on_line )
1610 msg_Warn( p_filter, "unbreakable string" );
1611 p_line->pp_glyphs[ i ] = NULL;
1612 return VLC_EGENERIC;
1614 *pi_pen_x = i_pen_x_start;
1616 p_line->i_width = line.xMax;
1617 p_line->i_height = __MAX( p_line->i_height,
1618 p_face->size->metrics.height >> 6 );
1619 p_line->pp_glyphs[ i ] = NULL;
1621 p_result->x = __MAX( p_result->x, line.xMax );
1622 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1623 i_yMax - i_yMin ) );
1628 *psz_unicode = '\n';
1630 psz_unicode = psz_unicode_start;
1631 *pi_pen_x = i_pen_x_start;
1639 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1640 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1642 i_previous = i_glyph_index;
1643 *pi_pen_x += p_face->glyph->advance.x >> 6;
1646 p_line->i_width = line.xMax;
1647 p_line->i_height = __MAX( p_line->i_height,
1648 p_face->size->metrics.height >> 6 );
1649 p_line->pp_glyphs[ i ] = NULL;
1651 p_result->x = __MAX( p_result->x, line.xMax );
1652 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1653 line.yMax - line.yMin ) );
1657 /* Get rid of any text processed - if necessary repositioning
1658 * at the start of a new line of text
1662 *psz_unicode_start = '\0';
1664 else if( psz_unicode > psz_unicode_start )
1666 for( i=0; psz_unicode[ i ]; i++ )
1667 psz_unicode_start[ i ] = psz_unicode[ i ];
1668 psz_unicode_start[ i ] = '\0';
1674 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1675 uint32_t **psz_text_out, uint32_t *pi_runs,
1676 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1677 ft_style_t *p_style )
1679 uint32_t i_string_length = 0;
1681 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1682 *psz_text_out += i_string_length;
1684 if( ppp_styles && ppi_run_lengths )
1690 *ppp_styles = (ft_style_t **)
1691 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1693 else if( *pi_runs == 1 )
1695 *ppp_styles = (ft_style_t **)
1696 malloc( *pi_runs * sizeof( ft_style_t * ) );
1699 /* We have just malloc'ed this memory successfully -
1700 * *pi_runs HAS to be within the memory area of *ppp_styles */
1703 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1707 if( *ppi_run_lengths )
1709 *ppi_run_lengths = (uint32_t *)
1710 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1712 else if( *pi_runs == 1 )
1714 *ppi_run_lengths = (uint32_t *)
1715 malloc( *pi_runs * sizeof( uint32_t ) );
1718 /* same remarks here */
1719 if( *ppi_run_lengths )
1721 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1724 /* If we couldn't use the p_style argument due to memory allocation
1725 * problems above, release it here.
1727 if( p_style ) DeleteStyle( p_style );
1730 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1734 for( k=0; k < p_sys->i_font_attachments; k++ )
1736 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1738 FT_Face p_face = NULL;
1740 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1748 bool match = !strcasecmp( p_face->family_name,
1749 p_style->psz_fontname );
1751 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1752 match = match && p_style->b_bold;
1754 match = match && !p_style->b_bold;
1756 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1757 match = match && p_style->b_italic;
1759 match = match && !p_style->b_italic;
1767 FT_Done_Face( p_face );
1772 return VLC_EGENERIC;
1775 static int ProcessLines( filter_t *p_filter,
1780 uint32_t *pi_run_lengths,
1781 ft_style_t **pp_styles,
1782 line_desc_t **pp_lines,
1784 FT_Vector *p_result,
1788 uint32_t *pi_k_run_lengths,
1789 uint32_t *pi_k_durations )
1791 filter_sys_t *p_sys = p_filter->p_sys;
1792 ft_style_t **pp_char_styles;
1793 int *p_new_positions = NULL;
1794 int8_t *p_levels = NULL;
1795 uint8_t *pi_karaoke_bar = NULL;
1799 /* Assign each character in the text string its style explicitly, so that
1800 * after the characters have been shuffled around by Fribidi, we can re-apply
1801 * the styles, and to simplify the calculation of runs within a line.
1803 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1804 if( !pp_char_styles )
1809 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1810 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1811 * we just won't be able to display the progress bar; at least we'll
1817 for( j = 0; j < i_runs; j++ )
1818 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1819 pp_char_styles[ i++ ] = pp_styles[ j ];
1821 #if defined(HAVE_FRIBIDI)
1823 ft_style_t **pp_char_styles_new;
1824 int *p_old_positions;
1825 uint32_t *p_fribidi_string;
1826 int start_pos, pos = 0;
1828 pp_char_styles_new = (ft_style_t **)
1829 malloc( i_len * sizeof( ft_style_t * ));
1831 p_fribidi_string = (uint32_t *)
1832 malloc( (i_len + 1) * sizeof(uint32_t) );
1833 p_old_positions = (int *)
1834 malloc( (i_len + 1) * sizeof( int ) );
1835 p_new_positions = (int *)
1836 malloc( (i_len + 1) * sizeof( int ) );
1837 p_levels = (int8_t *)
1838 malloc( (i_len + 1) * sizeof( int8_t ) );
1840 if( ! pp_char_styles_new ||
1841 ! p_fribidi_string ||
1842 ! p_old_positions ||
1843 ! p_new_positions ||
1847 free( p_old_positions );
1848 free( p_new_positions );
1849 free( p_fribidi_string );
1850 free( pp_char_styles_new );
1851 free( pi_karaoke_bar );
1853 free( pp_char_styles );
1857 /* Do bidi conversion line-by-line */
1860 while(pos < i_len) {
1861 if (psz_text[pos] != '\n')
1863 p_fribidi_string[pos] = psz_text[pos];
1864 pp_char_styles_new[pos] = pp_char_styles[pos];
1865 p_new_positions[pos] = pos;
1870 while(pos < i_len) {
1871 if (psz_text[pos] == '\n')
1875 if (pos > start_pos)
1877 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1878 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1879 pos - start_pos, &base_dir,
1880 (FriBidiChar*)p_fribidi_string + start_pos,
1881 p_new_positions + start_pos,
1883 p_levels + start_pos );
1884 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1886 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1887 p_old_positions[ j - start_pos ] ];
1888 p_new_positions[ j ] += start_pos;
1892 free( p_old_positions );
1893 free( pp_char_styles );
1894 pp_char_styles = pp_char_styles_new;
1895 psz_text = p_fribidi_string;
1896 p_fribidi_string[ i_len ] = 0;
1899 /* Work out the karaoke */
1900 if( pi_karaoke_bar )
1902 int64_t i_last_duration = 0;
1903 int64_t i_duration = 0;
1904 int64_t i_start_pos = 0;
1905 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1907 for( k = 0; k< i_k_runs; k++ )
1909 double fraction = 0.0;
1911 i_duration += pi_k_durations[ k ];
1913 if( i_duration < i_elapsed )
1915 /* Completely finished this run-length -
1916 * let it render normally */
1920 else if( i_elapsed < i_last_duration )
1922 /* Haven't got up to this segment yet -
1923 * render it completely in karaoke BG mode */
1929 /* Partway through this run */
1931 fraction = (double)(i_elapsed - i_last_duration) /
1932 (double)pi_k_durations[ k ];
1934 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1936 double shade = pi_k_run_lengths[ k ] * fraction;
1938 if( p_new_positions )
1939 j = p_new_positions[ i_start_pos + i ];
1941 j = i_start_pos + i;
1943 if( i < (uint32_t)shade )
1944 pi_karaoke_bar[ j ] = 0xff;
1945 else if( (double)i > shade )
1946 pi_karaoke_bar[ j ] = 0x00;
1949 shade -= (int)shade;
1950 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1951 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1955 i_last_duration = i_duration;
1956 i_start_pos += pi_k_run_lengths[ k ];
1960 free( p_new_positions );
1962 FT_Vector tmp_result;
1964 line_desc_t *p_line = NULL;
1965 line_desc_t *p_prev = NULL;
1971 p_result->x = p_result->y = 0;
1972 tmp_result.x = tmp_result.y = 0;
1975 for( k = 0; k <= (uint32_t) i_len; k++ )
1977 if( ( k == (uint32_t) i_len ) ||
1979 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1981 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1983 /* End of the current style run */
1984 FT_Face p_face = NULL;
1987 /* Look for a match amongst our attachments first */
1988 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1992 char *psz_fontfile = NULL;
1994 psz_fontfile = FontConfig_Select( NULL,
1995 p_style->psz_fontname,
1999 if( psz_fontfile && ! *psz_fontfile )
2001 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2002 " so using default font", p_style->psz_fontname,
2003 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2004 (p_style->b_bold ? "(Bold)" :
2005 (p_style->b_italic ? "(Italic)" : ""))) );
2006 free( psz_fontfile );
2007 psz_fontfile = NULL;
2012 if( FT_New_Face( p_sys->p_library,
2013 psz_fontfile, i_idx, &p_face ) )
2015 free( psz_fontfile );
2016 free( pp_char_styles );
2017 #if defined(HAVE_FRIBIDI)
2020 free( pi_karaoke_bar );
2021 return VLC_EGENERIC;
2023 free( psz_fontfile );
2027 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2029 /* We've loaded a font face which is unhelpful for actually
2030 * rendering text - fallback to the default one.
2032 FT_Done_Face( p_face );
2036 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2037 ft_encoding_unicode ) ||
2038 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2039 p_style->i_font_size ) )
2041 if( p_face ) FT_Done_Face( p_face );
2042 free( pp_char_styles );
2043 #if defined(HAVE_FRIBIDI)
2046 free( pi_karaoke_bar );
2047 return VLC_EGENERIC;
2049 p_sys->i_use_kerning =
2050 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2053 uint32_t *psz_unicode = (uint32_t *)
2054 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2057 if( p_face ) FT_Done_Face( p_face );
2058 free( pp_char_styles );
2059 free( psz_unicode );
2060 #if defined(HAVE_FRIBIDI)
2063 free( pi_karaoke_bar );
2066 memcpy( psz_unicode, psz_text + i_prev,
2067 sizeof( uint32_t ) * ( k - i_prev ) );
2068 psz_unicode[ k - i_prev ] = 0;
2069 while( *psz_unicode )
2073 if( !(p_line = NewLine( i_len - i_prev)) )
2075 if( p_face ) FT_Done_Face( p_face );
2076 free( pp_char_styles );
2077 free( psz_unicode );
2078 #if defined(HAVE_FRIBIDI)
2081 free( pi_karaoke_bar );
2084 /* New Color mode only works in YUVA rendering mode --
2085 * (RGB mode has palette constraints on it). We therefore
2086 * need to populate the legacy colour fields also.
2088 p_line->b_new_color_mode = true;
2089 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2090 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2091 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2092 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2093 p_line->p_next = NULL;
2095 i_pen_y += tmp_result.y;
2099 if( p_prev ) p_prev->p_next = p_line;
2100 else *pp_lines = p_line;
2103 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2104 p_style->i_font_color, p_style->b_underline,
2105 p_style->i_karaoke_bg_color,
2106 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2107 &tmp_result ) != VLC_SUCCESS )
2109 if( p_face ) FT_Done_Face( p_face );
2110 free( pp_char_styles );
2111 free( psz_unicode );
2112 #if defined(HAVE_FRIBIDI)
2115 free( pi_karaoke_bar );
2116 return VLC_EGENERIC;
2121 p_result->x = __MAX( p_result->x, tmp_result.x );
2122 p_result->y += tmp_result.y;
2127 if( *psz_unicode == '\n')
2131 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2133 *c_ptr = *(c_ptr+1);
2138 free( psz_unicode );
2139 if( p_face ) FT_Done_Face( p_face );
2143 free( pp_char_styles );
2144 #if defined(HAVE_FRIBIDI)
2149 p_result->x = __MAX( p_result->x, tmp_result.x );
2150 p_result->y += tmp_result.y;
2153 if( pi_karaoke_bar )
2156 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2158 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2160 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2164 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2166 /* 100% BG colour will render faster if we
2167 * instead make it 100% FG colour, so leave
2168 * the ratio alone and copy the value across
2170 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2174 if( pi_karaoke_bar[ i ] & 0x80 )
2176 /* Swap Left and Right sides over for Right aligned
2177 * language text (eg. Arabic, Hebrew)
2179 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2181 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2182 p_line->p_bg_rgb[ k ] = i_tmp;
2184 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2187 /* Jump over the '\n' at the line-end */
2190 free( pi_karaoke_bar );
2196 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2197 subpicture_region_t *p_region_in )
2199 int rv = VLC_SUCCESS;
2200 stream_t *p_sub = NULL;
2201 xml_reader_t *p_xml_reader = NULL;
2203 if( !p_region_in || !p_region_in->psz_html )
2204 return VLC_EGENERIC;
2206 /* Reset the default fontsize in case screen metrics have changed */
2207 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2209 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2210 (uint8_t *) p_region_in->psz_html,
2211 strlen( p_region_in->psz_html ),
2215 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2216 if( p_filter->p_sys->p_xml )
2218 bool b_karaoke = false;
2220 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2223 /* Look for Root Node */
2224 if( xml_ReaderRead( p_xml_reader ) == 1 )
2226 char *psz_node = xml_ReaderName( p_xml_reader );
2228 if( !strcasecmp( "karaoke", psz_node ) )
2230 /* We're going to have to render the text a number
2231 * of times to show the progress marker on the text.
2233 var_SetBool( p_filter, "text-rerender", true );
2236 else if( !strcasecmp( "text", psz_node ) )
2242 /* Only text and karaoke tags are supported */
2243 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2244 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2245 p_xml_reader = NULL;
2257 uint32_t i_runs = 0;
2258 uint32_t i_k_runs = 0;
2259 uint32_t *pi_run_lengths = NULL;
2260 uint32_t *pi_k_run_lengths = NULL;
2261 uint32_t *pi_k_durations = NULL;
2262 ft_style_t **pp_styles = NULL;
2264 line_desc_t *p_lines = NULL;
2266 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2267 sizeof( uint32_t ) );
2272 rv = ProcessNodes( p_filter, p_xml_reader,
2273 p_region_in->p_style, psz_text, &i_len,
2274 &i_runs, &pi_run_lengths, &pp_styles,
2276 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2279 p_region_out->i_x = p_region_in->i_x;
2280 p_region_out->i_y = p_region_in->i_y;
2282 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2284 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2285 pi_run_lengths, pp_styles, &p_lines, &result,
2286 b_karaoke, i_k_runs, pi_k_run_lengths,
2290 for( k=0; k<i_runs; k++)
2291 DeleteStyle( pp_styles[k] );
2293 free( pi_run_lengths );
2296 /* Don't attempt to render text that couldn't be layed out
2299 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2301 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2303 Render( p_filter, p_region_out, p_lines,
2304 result.x, result.y );
2308 RenderYUVA( p_filter, p_region_out, p_lines,
2309 result.x, result.y );
2313 FreeLines( p_lines );
2315 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2318 stream_Delete( p_sub );
2324 static char* FontConfig_Select( FcConfig* priv, const char* family,
2325 bool b_bold, bool b_italic, int *i_idx )
2328 FcPattern *pat, *p_pat;
2332 pat = FcPatternCreate();
2333 if (!pat) return NULL;
2335 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2336 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2337 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2338 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2340 FcDefaultSubstitute( pat );
2342 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2344 FcPatternDestroy( pat );
2348 p_pat = FcFontMatch( priv, pat, &result );
2349 FcPatternDestroy( pat );
2350 if( !p_pat ) return NULL;
2352 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2353 || ( val_b != FcTrue ) )
2355 FcPatternDestroy( p_pat );
2358 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2363 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2365 FcPatternDestroy( p_pat );
2370 if( strcasecmp((const char*)val_s, family ) != 0 )
2371 msg_Warn( p_filter, "fontconfig: selected font family is not"
2372 "the requested one: '%s' != '%s'\n",
2373 (const char*)val_s, family );
2376 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2378 FcPatternDestroy( p_pat );
2382 FcPatternDestroy( p_pat );
2383 return strdup( (const char*)val_s );
2387 static void FreeLine( line_desc_t *p_line )
2390 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2392 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2394 free( p_line->pp_glyphs );
2395 free( p_line->p_glyph_pos );
2396 free( p_line->p_fg_rgb );
2397 free( p_line->p_bg_rgb );
2398 free( p_line->p_fg_bg_ratio );
2399 free( p_line->pi_underline_offset );
2400 free( p_line->pi_underline_thickness );
2404 static void FreeLines( line_desc_t *p_lines )
2406 line_desc_t *p_line, *p_next;
2408 if( !p_lines ) return;
2410 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2412 p_next = p_line->p_next;
2417 static line_desc_t *NewLine( int i_count )
2419 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2421 if( !p_line ) return NULL;
2422 p_line->i_height = 0;
2423 p_line->i_width = 0;
2424 p_line->p_next = NULL;
2426 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2427 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2428 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2429 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2430 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2431 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2432 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2433 if( ( p_line->pp_glyphs == NULL ) ||
2434 ( p_line->p_glyph_pos == NULL ) ||
2435 ( p_line->p_fg_rgb == NULL ) ||
2436 ( p_line->p_bg_rgb == NULL ) ||
2437 ( p_line->p_fg_bg_ratio == NULL ) ||
2438 ( p_line->pi_underline_offset == NULL ) ||
2439 ( p_line->pi_underline_thickness == NULL ) )
2441 free( p_line->pi_underline_thickness );
2442 free( p_line->pi_underline_offset );
2443 free( p_line->p_fg_rgb );
2444 free( p_line->p_bg_rgb );
2445 free( p_line->p_fg_bg_ratio );
2446 free( p_line->p_glyph_pos );
2447 free( p_line->pp_glyphs );
2451 p_line->pp_glyphs[0] = NULL;
2452 p_line->b_new_color_mode = false;
2457 static int GetFontSize( filter_t *p_filter )
2459 filter_sys_t *p_sys = p_filter->p_sys;
2463 if( p_sys->i_default_font_size )
2465 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2466 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2468 i_size = p_sys->i_default_font_size;
2472 var_Get( p_filter, "freetype-rel-fontsize", &val );
2475 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2476 p_filter->p_sys->i_display_height =
2477 p_filter->fmt_out.video.i_height;
2482 msg_Warn( p_filter, "invalid fontsize, using 12" );
2483 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2484 i_size = 12 * val.i_int / 1000;
2491 static int SetFontSize( filter_t *p_filter, int i_size )
2493 filter_sys_t *p_sys = p_filter->p_sys;
2497 i_size = GetFontSize( p_filter );
2499 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2502 p_sys->i_font_size = i_size;
2504 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2506 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2507 return VLC_EGENERIC;
2513 static void YUVFromRGB( uint32_t i_argb,
2514 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2516 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2517 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2518 int i_blue = ( i_argb & 0x000000ff );
2520 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2521 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2522 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2523 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2524 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2525 -585 * i_blue + 4096 + 1048576) >> 13, 240);