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 "/Library/Fonts/Arial Black.ttf"
58 #define FC_DEFAULT_FONT "Arial Black"
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", false, 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 );
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" );
339 strcpy( psz_fontfamily, DEFAULT_FONT );
341 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
345 #ifdef HAVE_FONTCONFIG
346 /* Lets find some fontfile from freetype-font variable family */
348 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
352 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
353 _("Building font cache"),
354 _("Please wait while your font cache is rebuilt.\n"
355 "This should take less than few minutes."), NULL );
357 path = (char *)malloc( PATH_MAX + 1 );
358 /* Fontconfig doesnt seem to know where windows fonts are with
359 * current contribs. So just tell default windows font directory
360 * is the place to search fonts
362 GetWindowsDirectory( path, PATH_MAX + 1 );
363 strcat( path, "\\fonts" );
365 dialog_ProgressSet( p_dialog, NULL, 0.4 );
367 FcConfigAppFontAddDir( NULL , path );
372 dialog_ProgressSet( p_dialog, NULL, 0.5 );
376 msg_Dbg( p_filter, "Building font database.");
378 FcConfigBuildFonts( NULL );
381 msg_Dbg( p_filter, "Finished building font database." );
382 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
384 fontpattern = FcPatternCreate();
388 msg_Err( p_filter, "Creating fontpattern failed");
394 dialog_ProgressSet( p_dialog, NULL, 0.7 );
396 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
397 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
398 free( psz_fontsize );
400 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
402 msg_Err( p_filter, "FontSubstitute failed");
405 FcDefaultSubstitute( fontpattern );
409 dialog_ProgressSet( p_dialog, NULL, 0.8 );
411 /* testing fontresult here doesn't do any good really, but maybe it will
412 * in future as fontconfig code doesn't set it in all cases and just
413 * returns NULL or doesn't set to to Match on all Match cases.*/
414 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
415 if( !fontmatch || fontresult == FcResultNoMatch )
417 msg_Err( p_filter, "Fontmatching failed");
421 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
422 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
425 msg_Err( p_filter, "Failed to get fontfile");
429 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
430 p_sys->psz_fontfamily = strdup( psz_fontfamily );
434 dialog_ProgressSet( p_dialog, NULL, 1.0 );
435 dialog_ProgressDestroy( p_dialog );
441 #ifdef HAVE_FONTCONFIG
442 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
443 psz_fontfile = psz_fontfamily;
448 i_error = FT_Init_FreeType( &p_sys->p_library );
451 msg_Err( p_filter, "couldn't initialize freetype" );
455 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
456 fontindex, &p_sys->p_face );
458 if( i_error == FT_Err_Unknown_File_Format )
460 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
465 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
469 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
472 msg_Err( p_filter, "font has no unicode translation table" );
476 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
478 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
481 p_sys->pp_font_attachments = NULL;
482 p_sys->i_font_attachments = 0;
484 p_filter->pf_render_text = RenderText;
485 #ifdef HAVE_FONTCONFIG
486 p_filter->pf_render_html = RenderHtml;
487 FcPatternDestroy( fontmatch );
488 FcPatternDestroy( fontpattern );
490 p_filter->pf_render_html = NULL;
493 free( psz_fontfamily );
494 LoadFontsFromAttachments( p_filter );
499 #ifdef HAVE_FONTCONFIG
500 if( fontmatch ) FcPatternDestroy( fontmatch );
501 if( fontpattern ) FcPatternDestroy( fontpattern );
503 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
504 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
505 free( psz_fontfamily );
510 /*****************************************************************************
511 * Destroy: destroy Clone video thread output method
512 *****************************************************************************
513 * Clean up all data and library connections
514 *****************************************************************************/
515 static void Destroy( vlc_object_t *p_this )
517 filter_t *p_filter = (filter_t *)p_this;
518 filter_sys_t *p_sys = p_filter->p_sys;
520 if( p_sys->pp_font_attachments )
524 for( k = 0; k < p_sys->i_font_attachments; k++ )
525 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
527 free( p_sys->pp_font_attachments );
530 #ifdef HAVE_FONTCONFIG
531 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
532 free( p_sys->psz_fontfamily );
535 /* FcFini asserts calling the subfunction FcCacheFini()
536 * even if no other library functions have been made since FcInit(),
537 * so don't call it. */
539 FT_Done_Face( p_sys->p_face );
540 FT_Done_FreeType( p_sys->p_library );
544 /*****************************************************************************
545 * Make any TTF/OTF fonts present in the attachments of the media file
546 * and store them for later use by the FreeType Engine
547 *****************************************************************************/
548 static int LoadFontsFromAttachments( filter_t *p_filter )
550 filter_sys_t *p_sys = p_filter->p_sys;
551 input_thread_t *p_input;
552 input_attachment_t **pp_attachments;
553 int i_attachments_cnt;
555 int rv = VLC_SUCCESS;
557 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
561 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
563 vlc_object_release(p_input);
567 p_sys->i_font_attachments = 0;
568 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
569 if(! p_sys->pp_font_attachments )
572 for( k = 0; k < i_attachments_cnt; k++ )
574 input_attachment_t *p_attach = pp_attachments[k];
576 if( p_sys->pp_font_attachments )
578 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
579 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
580 ( p_attach->i_data > 0 ) &&
581 ( p_attach->p_data != NULL ) )
583 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
587 vlc_input_attachment_Delete( p_attach );
592 vlc_input_attachment_Delete( p_attach );
595 free( pp_attachments );
597 vlc_object_release(p_input);
602 /*****************************************************************************
603 * Render: place string in picture
604 *****************************************************************************
605 * This function merges the previously rendered freetype glyphs into a picture
606 *****************************************************************************/
607 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
608 line_desc_t *p_line, int i_width, int i_height )
610 VLC_UNUSED(p_filter);
611 static const uint8_t pi_gamma[16] =
612 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
613 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
617 int i, x, y, i_pitch;
618 uint8_t i_y; /* YUV values, derived from incoming RGB */
621 /* Create a new subpicture region */
622 memset( &fmt, 0, sizeof(video_format_t) );
623 fmt.i_chroma = VLC_CODEC_YUVP;
625 fmt.i_width = fmt.i_visible_width = i_width + 4;
626 fmt.i_height = fmt.i_visible_height = i_height + 4;
627 if( p_region->fmt.i_visible_width > 0 )
628 fmt.i_visible_width = p_region->fmt.i_visible_width;
629 if( p_region->fmt.i_visible_height > 0 )
630 fmt.i_visible_height = p_region->fmt.i_visible_height;
631 fmt.i_x_offset = fmt.i_y_offset = 0;
633 assert( !p_region->p_picture );
634 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
635 if( !p_region->p_picture )
639 /* Calculate text color components */
640 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
641 25 * p_line->i_blue + 128) >> 8) + 16;
642 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
643 112 * p_line->i_blue + 128) >> 8) + 128;
644 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
645 18 * p_line->i_blue + 128) >> 8) + 128;
648 fmt.p_palette->i_entries = 16;
649 for( i = 0; i < 8; i++ )
651 fmt.p_palette->palette[i][0] = 0;
652 fmt.p_palette->palette[i][1] = 0x80;
653 fmt.p_palette->palette[i][2] = 0x80;
654 fmt.p_palette->palette[i][3] = pi_gamma[i];
655 fmt.p_palette->palette[i][3] =
656 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
658 for( i = 8; i < fmt.p_palette->i_entries; i++ )
660 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
661 fmt.p_palette->palette[i][1] = i_u;
662 fmt.p_palette->palette[i][2] = i_v;
663 fmt.p_palette->palette[i][3] = pi_gamma[i];
664 fmt.p_palette->palette[i][3] =
665 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
668 p_dst = p_region->p_picture->Y_PIXELS;
669 i_pitch = p_region->p_picture->Y_PITCH;
671 /* Initialize the region pixels */
672 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
674 for( ; p_line != NULL; p_line = p_line->p_next )
676 int i_glyph_tmax = 0;
677 int i_bitmap_offset, i_offset, i_align_offset = 0;
678 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
680 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
681 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
684 if( p_line->i_width < i_width )
686 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
688 i_align_offset = i_width - p_line->i_width;
690 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
692 i_align_offset = ( i_width - p_line->i_width ) / 2;
696 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
698 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
700 i_offset = ( p_line->p_glyph_pos[ i ].y +
701 i_glyph_tmax - p_glyph->top + 2 ) *
702 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
705 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
707 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
709 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
711 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
718 /* Outlining (find something better than nearest neighbour filtering ?) */
721 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
722 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
723 uint8_t left, current;
725 for( y = 1; y < (int)fmt.i_height - 1; y++ )
727 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
728 p_dst += p_region->p_picture->Y_PITCH;
731 for( x = 1; x < (int)fmt.i_width - 1; x++ )
734 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
735 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;
739 memset( p_top, 0, fmt.i_width );
745 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
746 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
747 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
748 int i_glyph_tmax, int i_align_offset,
749 uint8_t i_y, uint8_t i_u, uint8_t i_v,
750 subpicture_region_t *p_region)
754 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
756 p_dst_y = p_region->p_picture->Y_PIXELS;
757 p_dst_u = p_region->p_picture->U_PIXELS;
758 p_dst_v = p_region->p_picture->V_PIXELS;
759 p_dst_a = p_region->p_picture->A_PIXELS;
760 i_pitch = p_region->p_picture->A_PITCH;
762 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
763 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
765 for( y = 0; y < i_line_thickness; y++ )
767 int i_extra = p_this_glyph->bitmap.width;
771 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
772 (p_this_glyph_pos->x + p_this_glyph->left);
774 for( x = 0; x < i_extra; x++ )
778 /* break the underline around the tails of any glyphs which cross it */
779 for( z = x - i_line_thickness;
780 z < x + i_line_thickness && b_ok;
783 if( p_next_glyph && ( z >= i_extra ) )
785 int i_row = i_line_offset + p_next_glyph->top + y;
787 if( ( p_next_glyph->bitmap.rows > i_row ) &&
788 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
793 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
795 int i_row = i_line_offset + p_this_glyph->top + y;
797 if( ( p_this_glyph->bitmap.rows > i_row ) &&
798 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
807 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
808 p_dst_u[i_offset+x] = i_u;
809 p_dst_v[i_offset+x] = i_v;
810 p_dst_a[i_offset+x] = 255;
817 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
819 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
820 int i_pitch = p_region->p_picture->A_PITCH;
823 for( ; p_line != NULL; p_line = p_line->p_next )
825 int i_glyph_tmax=0, i = 0;
826 int i_bitmap_offset, i_offset, i_align_offset = 0;
827 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
829 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
830 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
833 if( p_line->i_width < i_width )
835 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
837 i_align_offset = i_width - p_line->i_width;
839 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
841 i_align_offset = ( i_width - p_line->i_width ) / 2;
845 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
847 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
849 i_offset = ( p_line->p_glyph_pos[ i ].y +
850 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
851 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
852 i_align_offset +xoffset;
854 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
856 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
858 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
859 if( p_dst[i_offset+x] <
860 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
862 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
871 /*****************************************************************************
872 * Render: place string in picture
873 *****************************************************************************
874 * This function merges the previously rendered freetype glyphs into a picture
875 *****************************************************************************/
876 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
877 line_desc_t *p_line, int i_width, int i_height )
879 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
881 int i, x, y, i_pitch, i_alpha;
882 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
884 if( i_width == 0 || i_height == 0 )
887 /* Create a new subpicture region */
888 memset( &fmt, 0, sizeof(video_format_t) );
889 fmt.i_chroma = VLC_CODEC_YUVA;
891 fmt.i_width = fmt.i_visible_width = i_width + 6;
892 fmt.i_height = fmt.i_visible_height = i_height + 6;
893 if( p_region->fmt.i_visible_width > 0 )
894 fmt.i_visible_width = p_region->fmt.i_visible_width;
895 if( p_region->fmt.i_visible_height > 0 )
896 fmt.i_visible_height = p_region->fmt.i_visible_height;
897 fmt.i_x_offset = fmt.i_y_offset = 0;
899 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
900 if( !p_region->p_picture )
904 /* Calculate text color components */
905 YUVFromRGB( (p_line->i_red << 16) |
906 (p_line->i_green << 8) |
909 i_alpha = p_line->i_alpha;
911 p_dst_y = p_region->p_picture->Y_PIXELS;
912 p_dst_u = p_region->p_picture->U_PIXELS;
913 p_dst_v = p_region->p_picture->V_PIXELS;
914 p_dst_a = p_region->p_picture->A_PIXELS;
915 i_pitch = p_region->p_picture->A_PITCH;
917 /* Initialize the region pixels */
918 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
920 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
921 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
922 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
923 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
927 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
928 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
929 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
930 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
932 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
933 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
935 DrawBlack( p_line, i_width, p_region, 0, 0);
936 DrawBlack( p_line, i_width, p_region, -1, 0);
937 DrawBlack( p_line, i_width, p_region, 0, -1);
938 DrawBlack( p_line, i_width, p_region, 1, 0);
939 DrawBlack( p_line, i_width, p_region, 0, 1);
942 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
947 DrawBlack( p_line, i_width, p_region, 1, 1);
949 DrawBlack( p_line, i_width, p_region, -2, 0);
950 DrawBlack( p_line, i_width, p_region, 0, -2);
951 DrawBlack( p_line, i_width, p_region, 2, 0);
952 DrawBlack( p_line, i_width, p_region, 0, 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);
957 DrawBlack( p_line, i_width, p_region, 2, 2);
959 DrawBlack( p_line, i_width, p_region, -3, 0);
960 DrawBlack( p_line, i_width, p_region, 0, -3);
961 DrawBlack( p_line, i_width, p_region, 3, 0);
962 DrawBlack( p_line, i_width, p_region, 0, 3);
965 for( ; p_line != NULL; p_line = p_line->p_next )
967 int i_glyph_tmax = 0;
968 int i_bitmap_offset, i_offset, i_align_offset = 0;
969 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
971 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
972 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
975 if( p_line->i_width < i_width )
977 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
979 i_align_offset = i_width - p_line->i_width;
981 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
983 i_align_offset = ( i_width - p_line->i_width ) / 2;
987 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
989 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
991 i_offset = ( p_line->p_glyph_pos[ i ].y +
992 i_glyph_tmax - p_glyph->top + 3 ) *
993 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
996 if( p_line->b_new_color_mode )
998 /* Every glyph can (and in fact must) have its own color */
999 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1002 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1004 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1006 uint8_t i_y_local = i_y;
1007 uint8_t i_u_local = i_u;
1008 uint8_t i_v_local = i_v;
1010 if( p_line->p_fg_bg_ratio != 0x00 )
1012 int i_split = p_glyph->bitmap.width *
1013 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1017 YUVFromRGB( p_line->p_bg_rgb[ i ],
1018 &i_y_local, &i_u_local, &i_v_local );
1022 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1024 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1025 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1027 p_dst_u[i_offset+x] = i_u;
1028 p_dst_v[i_offset+x] = i_v;
1030 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1031 p_dst_a[i_offset+x] = 0xff;
1034 i_offset += i_pitch;
1037 if( p_line->pi_underline_thickness[ i ] )
1039 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1040 p_line->pi_underline_offset[ i ],
1041 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1042 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1043 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1044 i_glyph_tmax, i_align_offset,
1051 /* Apply the alpha setting */
1052 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1053 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1059 * This function renders a text subpicture region into another one.
1060 * It also calculates the size needed for this string, and renders the
1061 * needed glyphs into memory. It is used as pf_add_string callback in
1062 * the vout method by this module
1064 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1065 subpicture_region_t *p_region_in )
1067 filter_sys_t *p_sys = p_filter->p_sys;
1068 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1069 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1070 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1071 int i_string_length;
1073 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1074 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1084 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1085 psz_string = p_region_in->psz_text;
1086 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1088 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1089 i_scale = val.i_int;
1091 if( p_region_in->p_style )
1093 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1094 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1095 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1099 i_font_color = p_sys->i_font_color;
1100 i_font_alpha = 255 - p_sys->i_font_opacity;
1101 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1104 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1105 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1106 SetFontSize( p_filter, i_font_size );
1108 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1109 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1110 i_blue = i_font_color & 0x000000FF;
1112 result.x = result.y = 0;
1113 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1115 psz_unicode = psz_unicode_orig =
1116 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1117 if( psz_unicode == NULL )
1119 #if defined(WORDS_BIGENDIAN)
1120 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1122 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1124 if( iconv_handle == (vlc_iconv_t)-1 )
1126 msg_Warn( p_filter, "unable to do conversion" );
1132 const char *p_in_buffer = psz_string;
1133 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1134 i_in_bytes = strlen( psz_string );
1135 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1136 i_out_bytes_left = i_out_bytes;
1137 p_out_buffer = (char *)psz_unicode;
1138 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1140 &p_out_buffer, &i_out_bytes_left );
1142 vlc_iconv_close( iconv_handle );
1146 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1147 "bytes left %u", (unsigned)i_in_bytes );
1150 *(uint32_t*)p_out_buffer = 0;
1151 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1154 #if defined(HAVE_FRIBIDI)
1156 uint32_t *p_fribidi_string;
1157 int32_t start_pos, pos = 0;
1159 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1160 if( !p_fribidi_string )
1163 /* Do bidi conversion line-by-line */
1164 while( pos < i_string_length )
1166 while( pos < i_string_length )
1168 i_char = psz_unicode[pos];
1169 if (i_char != '\r' && i_char != '\n')
1171 p_fribidi_string[pos] = i_char;
1175 while( pos < i_string_length )
1177 i_char = psz_unicode[pos];
1178 if (i_char == '\r' || i_char == '\n')
1182 if (pos > start_pos)
1184 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1185 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1188 (FriBidiChar*)p_fribidi_string + start_pos,
1193 free( psz_unicode_orig );
1194 psz_unicode = psz_unicode_orig = p_fribidi_string;
1195 p_fribidi_string[ i_string_length ] = 0;
1199 /* Calculate relative glyph positions and a bounding box for the
1201 if( !(p_line = NewLine( strlen( psz_string ))) )
1204 i_pen_x = i_pen_y = 0;
1206 psz_line_start = psz_unicode;
1208 #define face p_sys->p_face
1209 #define glyph face->glyph
1211 while( *psz_unicode )
1213 i_char = *psz_unicode++;
1214 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1219 if( i_char == '\n' )
1221 psz_line_start = psz_unicode;
1222 if( !(p_next = NewLine( strlen( psz_string ))) )
1224 p_line->p_next = p_next;
1225 p_line->i_width = line.xMax;
1226 p_line->i_height = face->size->metrics.height >> 6;
1227 p_line->pp_glyphs[ i ] = NULL;
1228 p_line->i_alpha = i_font_alpha;
1229 p_line->i_red = i_red;
1230 p_line->i_green = i_green;
1231 p_line->i_blue = i_blue;
1234 result.x = __MAX( result.x, line.xMax );
1235 result.y += face->size->metrics.height >> 6;
1238 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1239 i_pen_y += face->size->metrics.height >> 6;
1241 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1246 i_glyph_index = FT_Get_Char_Index( face, i_char );
1247 if( p_sys->i_use_kerning && i_glyph_index
1251 FT_Get_Kerning( face, i_previous, i_glyph_index,
1252 ft_kerning_default, &delta );
1253 i_pen_x += delta.x >> 6;
1256 p_line->p_glyph_pos[ i ].x = i_pen_x;
1257 p_line->p_glyph_pos[ i ].y = i_pen_y;
1258 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1261 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1265 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1268 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1272 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1273 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1276 FT_Done_Glyph( tmp_glyph );
1279 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1282 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1283 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1284 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1286 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1287 p_line->pp_glyphs[ i ] = NULL;
1289 p_line = NewLine( strlen( psz_string ));
1290 if( p_prev ) p_prev->p_next = p_line;
1291 else p_lines = p_line;
1293 uint32_t *psz_unicode_saved = psz_unicode;
1294 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1298 if( psz_unicode == psz_line_start )
1299 { /* try harder to break that line */
1300 psz_unicode = psz_unicode_saved;
1301 while( psz_unicode > psz_line_start &&
1302 *psz_unicode != '_' && *psz_unicode != '/' &&
1303 *psz_unicode != '\\' && *psz_unicode != '.' )
1308 if( psz_unicode == psz_line_start )
1310 msg_Warn( p_filter, "unbreakable string" );
1315 *psz_unicode = '\n';
1317 psz_unicode = psz_line_start;
1320 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1323 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1324 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1326 i_previous = i_glyph_index;
1327 i_pen_x += glyph->advance.x >> 6;
1331 p_line->i_width = line.xMax;
1332 p_line->i_height = face->size->metrics.height >> 6;
1333 p_line->pp_glyphs[ i ] = NULL;
1334 p_line->i_alpha = i_font_alpha;
1335 p_line->i_red = i_red;
1336 p_line->i_green = i_green;
1337 p_line->i_blue = i_blue;
1338 result.x = __MAX( result.x, line.xMax );
1339 result.y += line.yMax - line.yMin;
1344 p_region_out->i_x = p_region_in->i_x;
1345 p_region_out->i_y = p_region_in->i_y;
1347 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1348 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1350 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1352 free( psz_unicode_orig );
1353 FreeLines( p_lines );
1357 free( psz_unicode_orig );
1358 FreeLines( p_lines );
1359 return VLC_EGENERIC;
1362 #ifdef HAVE_FONTCONFIG
1363 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1364 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1365 bool b_italic, bool b_uline )
1367 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1371 p_style->i_font_size = i_font_size;
1372 p_style->i_font_color = i_font_color;
1373 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1374 p_style->b_italic = b_italic;
1375 p_style->b_bold = b_bold;
1376 p_style->b_underline = b_uline;
1378 p_style->psz_fontname = strdup( psz_fontname );
1383 static void DeleteStyle( ft_style_t *p_style )
1387 free( p_style->psz_fontname );
1392 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1399 if(( s1->i_font_size == s2->i_font_size ) &&
1400 ( s1->i_font_color == s2->i_font_color ) &&
1401 ( s1->b_italic == s2->b_italic ) &&
1402 ( s1->b_bold == s2->b_bold ) &&
1403 ( s1->b_underline == s2->b_underline ) &&
1404 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1411 static void IconvText( filter_t *p_filter, const char *psz_string,
1412 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1414 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1416 /* If memory hasn't been allocated for our output string, allocate it here
1417 * - the calling function must now be responsible for freeing it.
1419 if( !*ppsz_unicode )
1420 *ppsz_unicode = (uint32_t *)
1421 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1423 /* We don't need to handle a NULL pointer in *ppsz_unicode
1424 * if we are instead testing for a non NULL value like we are here */
1428 #if defined(WORDS_BIGENDIAN)
1429 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1431 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1433 if( iconv_handle != (vlc_iconv_t)-1 )
1435 char *p_in_buffer, *p_out_buffer;
1436 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1437 i_in_bytes = strlen( psz_string );
1438 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1439 i_out_bytes_left = i_out_bytes;
1440 p_in_buffer = (char *) psz_string;
1441 p_out_buffer = (char *) *ppsz_unicode;
1442 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1443 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1445 vlc_iconv_close( iconv_handle );
1449 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1450 "bytes left %u", (unsigned)i_in_bytes );
1454 *(uint32_t*)p_out_buffer = 0;
1456 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1461 msg_Warn( p_filter, "unable to do conversion" );
1466 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1467 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1470 ft_style_t *p_style = NULL;
1472 char *psz_fontname = NULL;
1473 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1474 uint32_t i_karaoke_bg_color = i_font_color;
1475 int i_font_size = p_sys->i_font_size;
1477 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1478 &i_font_color, &i_karaoke_bg_color ))
1480 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1481 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1486 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1487 bool b_uline, int i_karaoke_bgcolor,
1488 line_desc_t *p_line, uint32_t *psz_unicode,
1489 int *pi_pen_x, int i_pen_y, int *pi_start,
1490 FT_Vector *p_result )
1495 bool b_first_on_line = true;
1498 int i_pen_x_start = *pi_pen_x;
1500 uint32_t *psz_unicode_start = psz_unicode;
1502 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1504 /* Account for part of line already in position */
1505 for( i=0; i<*pi_start; i++ )
1509 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1510 ft_glyph_bbox_pixels, &glyph_size );
1512 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1513 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1514 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1515 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1521 b_first_on_line = false;
1523 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1529 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1530 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1534 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1535 ft_kerning_default, &delta );
1536 *pi_pen_x += delta.x >> 6;
1538 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1539 p_line->p_glyph_pos[ i ].y = i_pen_y;
1541 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1545 "unable to render text FT_Load_Glyph returned %d", i_error );
1546 p_line->pp_glyphs[ i ] = NULL;
1547 return VLC_EGENERIC;
1549 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1553 "unable to render text FT_Get_Glyph returned %d", i_error );
1554 p_line->pp_glyphs[ i ] = NULL;
1555 return VLC_EGENERIC;
1557 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1558 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1561 FT_Done_Glyph( tmp_glyph );
1566 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1567 p_face->size->metrics.y_scale));
1568 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1569 p_face->size->metrics.y_scale));
1571 p_line->pi_underline_offset[ i ] =
1572 ( aOffset < 0 ) ? -aOffset : aOffset;
1573 p_line->pi_underline_thickness[ i ] =
1574 ( aSize < 0 ) ? -aSize : aSize;
1576 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1577 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1578 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1579 p_line->p_fg_bg_ratio[ i ] = 0x00;
1581 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1582 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1583 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1585 for( ; i >= *pi_start; i-- )
1586 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1589 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1593 if( psz_unicode == psz_unicode_start )
1595 if( b_first_on_line )
1597 msg_Warn( p_filter, "unbreakable string" );
1598 p_line->pp_glyphs[ i ] = NULL;
1599 return VLC_EGENERIC;
1601 *pi_pen_x = i_pen_x_start;
1603 p_line->i_width = line.xMax;
1604 p_line->i_height = __MAX( p_line->i_height,
1605 p_face->size->metrics.height >> 6 );
1606 p_line->pp_glyphs[ i ] = NULL;
1608 p_result->x = __MAX( p_result->x, line.xMax );
1609 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1610 i_yMax - i_yMin ) );
1615 *psz_unicode = '\n';
1617 psz_unicode = psz_unicode_start;
1618 *pi_pen_x = i_pen_x_start;
1626 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1627 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1629 i_previous = i_glyph_index;
1630 *pi_pen_x += p_face->glyph->advance.x >> 6;
1633 p_line->i_width = line.xMax;
1634 p_line->i_height = __MAX( p_line->i_height,
1635 p_face->size->metrics.height >> 6 );
1636 p_line->pp_glyphs[ i ] = NULL;
1638 p_result->x = __MAX( p_result->x, line.xMax );
1639 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1640 line.yMax - line.yMin ) );
1644 /* Get rid of any text processed - if necessary repositioning
1645 * at the start of a new line of text
1649 *psz_unicode_start = '\0';
1651 else if( psz_unicode > psz_unicode_start )
1653 for( i=0; psz_unicode[ i ]; i++ )
1654 psz_unicode_start[ i ] = psz_unicode[ i ];
1655 psz_unicode_start[ i ] = '\0';
1661 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1662 uint32_t **psz_text_out, uint32_t *pi_runs,
1663 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1664 ft_style_t *p_style )
1666 uint32_t i_string_length = 0;
1668 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1669 *psz_text_out += i_string_length;
1671 if( ppp_styles && ppi_run_lengths )
1677 *ppp_styles = (ft_style_t **)
1678 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1680 else if( *pi_runs == 1 )
1682 *ppp_styles = (ft_style_t **)
1683 malloc( *pi_runs * sizeof( ft_style_t * ) );
1686 /* We have just malloc'ed this memory successfully -
1687 * *pi_runs HAS to be within the memory area of *ppp_styles */
1690 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1694 if( *ppi_run_lengths )
1696 *ppi_run_lengths = (uint32_t *)
1697 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1699 else if( *pi_runs == 1 )
1701 *ppi_run_lengths = (uint32_t *)
1702 malloc( *pi_runs * sizeof( uint32_t ) );
1705 /* same remarks here */
1706 if( *ppi_run_lengths )
1708 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1711 /* If we couldn't use the p_style argument due to memory allocation
1712 * problems above, release it here.
1714 if( p_style ) DeleteStyle( p_style );
1717 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1721 for( k=0; k < p_sys->i_font_attachments; k++ )
1723 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1725 FT_Face p_face = NULL;
1727 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1735 bool match = !strcasecmp( p_face->family_name,
1736 p_style->psz_fontname );
1738 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1739 match = match && p_style->b_bold;
1741 match = match && !p_style->b_bold;
1743 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1744 match = match && p_style->b_italic;
1746 match = match && !p_style->b_italic;
1754 FT_Done_Face( p_face );
1759 return VLC_EGENERIC;
1762 static int ProcessLines( filter_t *p_filter,
1767 uint32_t *pi_run_lengths,
1768 ft_style_t **pp_styles,
1769 line_desc_t **pp_lines,
1771 FT_Vector *p_result,
1775 uint32_t *pi_k_run_lengths,
1776 uint32_t *pi_k_durations )
1778 filter_sys_t *p_sys = p_filter->p_sys;
1779 ft_style_t **pp_char_styles;
1780 int *p_new_positions = NULL;
1781 int8_t *p_levels = NULL;
1782 uint8_t *pi_karaoke_bar = NULL;
1786 /* Assign each character in the text string its style explicitly, so that
1787 * after the characters have been shuffled around by Fribidi, we can re-apply
1788 * the styles, and to simplify the calculation of runs within a line.
1790 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1791 if( !pp_char_styles )
1796 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1797 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1798 * we just won't be able to display the progress bar; at least we'll
1804 for( j = 0; j < i_runs; j++ )
1805 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1806 pp_char_styles[ i++ ] = pp_styles[ j ];
1808 #if defined(HAVE_FRIBIDI)
1810 ft_style_t **pp_char_styles_new;
1811 int *p_old_positions;
1812 uint32_t *p_fribidi_string;
1813 int start_pos, pos = 0;
1815 pp_char_styles_new = (ft_style_t **)
1816 malloc( i_len * sizeof( ft_style_t * ));
1818 p_fribidi_string = (uint32_t *)
1819 malloc( (i_len + 1) * sizeof(uint32_t) );
1820 p_old_positions = (int *)
1821 malloc( (i_len + 1) * sizeof( int ) );
1822 p_new_positions = (int *)
1823 malloc( (i_len + 1) * sizeof( int ) );
1824 p_levels = (int8_t *)
1825 malloc( (i_len + 1) * sizeof( int8_t ) );
1827 if( ! pp_char_styles_new ||
1828 ! p_fribidi_string ||
1829 ! p_old_positions ||
1830 ! p_new_positions ||
1834 free( p_old_positions );
1835 free( p_new_positions );
1836 free( p_fribidi_string );
1837 free( pp_char_styles_new );
1838 free( pi_karaoke_bar );
1840 free( pp_char_styles );
1844 /* Do bidi conversion line-by-line */
1847 while(pos < i_len) {
1848 if (psz_text[pos] != '\n')
1850 p_fribidi_string[pos] = psz_text[pos];
1851 pp_char_styles_new[pos] = pp_char_styles[pos];
1852 p_new_positions[pos] = pos;
1857 while(pos < i_len) {
1858 if (psz_text[pos] == '\n')
1862 if (pos > start_pos)
1864 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1865 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1866 pos - start_pos, &base_dir,
1867 (FriBidiChar*)p_fribidi_string + start_pos,
1868 p_new_positions + start_pos,
1870 p_levels + start_pos );
1871 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1873 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1874 p_old_positions[ j - start_pos ] ];
1875 p_new_positions[ j ] += start_pos;
1879 free( p_old_positions );
1880 free( pp_char_styles );
1881 pp_char_styles = pp_char_styles_new;
1882 psz_text = p_fribidi_string;
1883 p_fribidi_string[ i_len ] = 0;
1886 /* Work out the karaoke */
1887 if( pi_karaoke_bar )
1889 int64_t i_last_duration = 0;
1890 int64_t i_duration = 0;
1891 int64_t i_start_pos = 0;
1892 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1894 for( k = 0; k< i_k_runs; k++ )
1896 double fraction = 0.0;
1898 i_duration += pi_k_durations[ k ];
1900 if( i_duration < i_elapsed )
1902 /* Completely finished this run-length -
1903 * let it render normally */
1907 else if( i_elapsed < i_last_duration )
1909 /* Haven't got up to this segment yet -
1910 * render it completely in karaoke BG mode */
1916 /* Partway through this run */
1918 fraction = (double)(i_elapsed - i_last_duration) /
1919 (double)pi_k_durations[ k ];
1921 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1923 double shade = pi_k_run_lengths[ k ] * fraction;
1925 if( p_new_positions )
1926 j = p_new_positions[ i_start_pos + i ];
1928 j = i_start_pos + i;
1930 if( i < (uint32_t)shade )
1931 pi_karaoke_bar[ j ] = 0xff;
1932 else if( (double)i > shade )
1933 pi_karaoke_bar[ j ] = 0x00;
1936 shade -= (int)shade;
1937 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1938 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1942 i_last_duration = i_duration;
1943 i_start_pos += pi_k_run_lengths[ k ];
1947 free( p_new_positions );
1949 FT_Vector tmp_result;
1951 line_desc_t *p_line = NULL;
1952 line_desc_t *p_prev = NULL;
1958 p_result->x = p_result->y = 0;
1959 tmp_result.x = tmp_result.y = 0;
1962 for( k = 0; k <= (uint32_t) i_len; k++ )
1964 if( ( k == (uint32_t) i_len ) ||
1966 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1968 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1970 /* End of the current style run */
1971 FT_Face p_face = NULL;
1974 /* Look for a match amongst our attachments first */
1975 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1979 char *psz_fontfile = NULL;
1981 psz_fontfile = FontConfig_Select( NULL,
1982 p_style->psz_fontname,
1986 if( psz_fontfile && ! *psz_fontfile )
1988 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1989 " so using default font", p_style->psz_fontname,
1990 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1991 (p_style->b_bold ? "(Bold)" :
1992 (p_style->b_italic ? "(Italic)" : ""))) );
1993 free( psz_fontfile );
1994 psz_fontfile = NULL;
1999 if( FT_New_Face( p_sys->p_library,
2000 psz_fontfile, i_idx, &p_face ) )
2002 free( psz_fontfile );
2003 free( pp_char_styles );
2004 #if defined(HAVE_FRIBIDI)
2007 free( pi_karaoke_bar );
2008 return VLC_EGENERIC;
2010 free( psz_fontfile );
2014 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2016 /* We've loaded a font face which is unhelpful for actually
2017 * rendering text - fallback to the default one.
2019 FT_Done_Face( p_face );
2023 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2024 ft_encoding_unicode ) ||
2025 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2026 p_style->i_font_size ) )
2028 if( p_face ) FT_Done_Face( p_face );
2029 free( pp_char_styles );
2030 #if defined(HAVE_FRIBIDI)
2033 free( pi_karaoke_bar );
2034 return VLC_EGENERIC;
2036 p_sys->i_use_kerning =
2037 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2040 uint32_t *psz_unicode = (uint32_t *)
2041 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2044 if( p_face ) FT_Done_Face( p_face );
2045 free( pp_char_styles );
2046 free( psz_unicode );
2047 #if defined(HAVE_FRIBIDI)
2050 free( pi_karaoke_bar );
2053 memcpy( psz_unicode, psz_text + i_prev,
2054 sizeof( uint32_t ) * ( k - i_prev ) );
2055 psz_unicode[ k - i_prev ] = 0;
2056 while( *psz_unicode )
2060 if( !(p_line = NewLine( i_len - i_prev)) )
2062 if( p_face ) FT_Done_Face( p_face );
2063 free( pp_char_styles );
2064 free( psz_unicode );
2065 #if defined(HAVE_FRIBIDI)
2068 free( pi_karaoke_bar );
2071 /* New Color mode only works in YUVA rendering mode --
2072 * (RGB mode has palette constraints on it). We therefore
2073 * need to populate the legacy colour fields also.
2075 p_line->b_new_color_mode = true;
2076 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2077 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2078 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2079 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2080 p_line->p_next = NULL;
2082 i_pen_y += tmp_result.y;
2086 if( p_prev ) p_prev->p_next = p_line;
2087 else *pp_lines = p_line;
2090 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2091 p_style->i_font_color, p_style->b_underline,
2092 p_style->i_karaoke_bg_color,
2093 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2094 &tmp_result ) != VLC_SUCCESS )
2096 if( p_face ) FT_Done_Face( p_face );
2097 free( pp_char_styles );
2098 free( psz_unicode );
2099 #if defined(HAVE_FRIBIDI)
2102 free( pi_karaoke_bar );
2103 return VLC_EGENERIC;
2108 p_result->x = __MAX( p_result->x, tmp_result.x );
2109 p_result->y += tmp_result.y;
2114 if( *psz_unicode == '\n')
2118 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2120 *c_ptr = *(c_ptr+1);
2125 free( psz_unicode );
2126 if( p_face ) FT_Done_Face( p_face );
2130 free( pp_char_styles );
2131 #if defined(HAVE_FRIBIDI)
2136 p_result->x = __MAX( p_result->x, tmp_result.x );
2137 p_result->y += tmp_result.y;
2140 if( pi_karaoke_bar )
2143 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2145 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2147 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2151 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2153 /* 100% BG colour will render faster if we
2154 * instead make it 100% FG colour, so leave
2155 * the ratio alone and copy the value across
2157 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2161 if( pi_karaoke_bar[ i ] & 0x80 )
2163 /* Swap Left and Right sides over for Right aligned
2164 * language text (eg. Arabic, Hebrew)
2166 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2168 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2169 p_line->p_bg_rgb[ k ] = i_tmp;
2171 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2174 /* Jump over the '\n' at the line-end */
2177 free( pi_karaoke_bar );
2183 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2184 subpicture_region_t *p_region_in )
2186 int rv = VLC_SUCCESS;
2187 stream_t *p_sub = NULL;
2188 xml_reader_t *p_xml_reader = NULL;
2190 if( !p_region_in || !p_region_in->psz_html )
2191 return VLC_EGENERIC;
2193 /* Reset the default fontsize in case screen metrics have changed */
2194 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2196 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2197 (uint8_t *) p_region_in->psz_html,
2198 strlen( p_region_in->psz_html ),
2202 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2203 if( p_filter->p_sys->p_xml )
2205 bool b_karaoke = false;
2207 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2210 /* Look for Root Node */
2211 if( xml_ReaderRead( p_xml_reader ) == 1 )
2213 char *psz_node = xml_ReaderName( p_xml_reader );
2215 if( !strcasecmp( "karaoke", psz_node ) )
2217 /* We're going to have to render the text a number
2218 * of times to show the progress marker on the text.
2220 var_SetBool( p_filter, "text-rerender", true );
2223 else if( !strcasecmp( "text", psz_node ) )
2229 /* Only text and karaoke tags are supported */
2230 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2231 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2232 p_xml_reader = NULL;
2244 uint32_t i_runs = 0;
2245 uint32_t i_k_runs = 0;
2246 uint32_t *pi_run_lengths = NULL;
2247 uint32_t *pi_k_run_lengths = NULL;
2248 uint32_t *pi_k_durations = NULL;
2249 ft_style_t **pp_styles = NULL;
2251 line_desc_t *p_lines = NULL;
2253 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2254 sizeof( uint32_t ) );
2259 rv = ProcessNodes( p_filter, p_xml_reader,
2260 p_region_in->p_style, psz_text, &i_len,
2261 &i_runs, &pi_run_lengths, &pp_styles,
2263 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2266 p_region_out->i_x = p_region_in->i_x;
2267 p_region_out->i_y = p_region_in->i_y;
2269 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2271 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2272 pi_run_lengths, pp_styles, &p_lines, &result,
2273 b_karaoke, i_k_runs, pi_k_run_lengths,
2277 for( k=0; k<i_runs; k++)
2278 DeleteStyle( pp_styles[k] );
2280 free( pi_run_lengths );
2283 /* Don't attempt to render text that couldn't be layed out
2286 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2288 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2290 Render( p_filter, p_region_out, p_lines,
2291 result.x, result.y );
2295 RenderYUVA( p_filter, p_region_out, p_lines,
2296 result.x, result.y );
2300 FreeLines( p_lines );
2302 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2305 stream_Delete( p_sub );
2311 static char* FontConfig_Select( FcConfig* priv, const char* family,
2312 bool b_bold, bool b_italic, int *i_idx )
2315 FcPattern *pat, *p_pat;
2319 pat = FcPatternCreate();
2320 if (!pat) return NULL;
2322 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2323 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2324 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2325 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2327 FcDefaultSubstitute( pat );
2329 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2331 FcPatternDestroy( pat );
2335 p_pat = FcFontMatch( priv, pat, &result );
2336 FcPatternDestroy( pat );
2337 if( !p_pat ) return NULL;
2339 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2340 || ( val_b != FcTrue ) )
2342 FcPatternDestroy( p_pat );
2345 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2350 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2352 FcPatternDestroy( p_pat );
2357 if( strcasecmp((const char*)val_s, family ) != 0 )
2358 msg_Warn( p_filter, "fontconfig: selected font family is not"
2359 "the requested one: '%s' != '%s'\n",
2360 (const char*)val_s, family );
2363 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2365 FcPatternDestroy( p_pat );
2369 FcPatternDestroy( p_pat );
2370 return strdup( (const char*)val_s );
2374 static void FreeLine( line_desc_t *p_line )
2377 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2379 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2381 free( p_line->pp_glyphs );
2382 free( p_line->p_glyph_pos );
2383 free( p_line->p_fg_rgb );
2384 free( p_line->p_bg_rgb );
2385 free( p_line->p_fg_bg_ratio );
2386 free( p_line->pi_underline_offset );
2387 free( p_line->pi_underline_thickness );
2391 static void FreeLines( line_desc_t *p_lines )
2393 line_desc_t *p_line, *p_next;
2395 if( !p_lines ) return;
2397 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2399 p_next = p_line->p_next;
2404 static line_desc_t *NewLine( int i_count )
2406 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2408 if( !p_line ) return NULL;
2409 p_line->i_height = 0;
2410 p_line->i_width = 0;
2411 p_line->p_next = NULL;
2413 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2414 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2415 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2416 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2417 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2418 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2419 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2420 if( ( p_line->pp_glyphs == NULL ) ||
2421 ( p_line->p_glyph_pos == NULL ) ||
2422 ( p_line->p_fg_rgb == NULL ) ||
2423 ( p_line->p_bg_rgb == NULL ) ||
2424 ( p_line->p_fg_bg_ratio == NULL ) ||
2425 ( p_line->pi_underline_offset == NULL ) ||
2426 ( p_line->pi_underline_thickness == NULL ) )
2428 free( p_line->pi_underline_thickness );
2429 free( p_line->pi_underline_offset );
2430 free( p_line->p_fg_rgb );
2431 free( p_line->p_bg_rgb );
2432 free( p_line->p_fg_bg_ratio );
2433 free( p_line->p_glyph_pos );
2434 free( p_line->pp_glyphs );
2438 p_line->pp_glyphs[0] = NULL;
2439 p_line->b_new_color_mode = false;
2444 static int GetFontSize( filter_t *p_filter )
2446 filter_sys_t *p_sys = p_filter->p_sys;
2450 if( p_sys->i_default_font_size )
2452 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2453 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2455 i_size = p_sys->i_default_font_size;
2459 var_Get( p_filter, "freetype-rel-fontsize", &val );
2462 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2463 p_filter->p_sys->i_display_height =
2464 p_filter->fmt_out.video.i_height;
2469 msg_Warn( p_filter, "invalid fontsize, using 12" );
2470 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2471 i_size = 12 * val.i_int / 1000;
2478 static int SetFontSize( filter_t *p_filter, int i_size )
2480 filter_sys_t *p_sys = p_filter->p_sys;
2484 i_size = GetFontSize( p_filter );
2486 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2489 p_sys->i_font_size = i_size;
2491 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2493 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2494 return VLC_EGENERIC;
2500 static void YUVFromRGB( uint32_t i_argb,
2501 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2503 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2504 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2505 int i_blue = ( i_argb & 0x000000ff );
2507 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2508 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2509 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2510 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2511 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2512 -585 * i_blue + 4096 + 1048576) >> 13, 240);