1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
42 #include <vlc_dialog.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
57 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
58 #define FC_DEFAULT_FONT "Lucida Grande"
59 #elif defined( SYS_BEOS )
60 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
61 #define FC_DEFAULT_FONT "Swiss"
62 #elif defined( WIN32 )
63 #define DEFAULT_FONT "" /* Default font found at run-time */
64 #define FC_DEFAULT_FONT "Arial"
66 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
67 #define FC_DEFAULT_FONT "Serif Bold"
70 #if defined(HAVE_FRIBIDI)
71 #include <fribidi/fribidi.h>
74 #ifdef HAVE_FONTCONFIG
75 #include <fontconfig/fontconfig.h>
77 #define DEFAULT_FONT FC_DEFAULT_FONT
82 /*****************************************************************************
84 *****************************************************************************/
85 static int Create ( vlc_object_t * );
86 static void Destroy( vlc_object_t * );
88 #define FONT_TEXT N_("Font")
90 #ifdef HAVE_FONTCONFIG
91 #define FONT_LONGTEXT N_("Font family for the font you want to use")
93 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
96 #define FONTSIZE_TEXT N_("Font size in pixels")
97 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
98 "that will be rendered on the video. " \
99 "If set to something different than 0 this option will override the " \
100 "relative font size." )
101 #define OPACITY_TEXT N_("Opacity")
102 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
103 "text that will be rendered on the video. 0 = transparent, " \
104 "255 = totally opaque. " )
105 #define COLOR_TEXT N_("Text default color")
106 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
107 "the video. This must be an hexadecimal (like HTML colors). The first two "\
108 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
109 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
110 #define FONTSIZER_TEXT N_("Relative font size")
111 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
112 "fonts that will be rendered on the video. If absolute font size is set, "\
113 "relative size will be overriden." )
115 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
116 static const char *const ppsz_sizes_text[] = {
117 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
118 #define YUVP_TEXT N_("Use YUVP renderer")
119 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
120 "This option is only needed if you want to encode into DVB subtitles" )
121 #define EFFECT_TEXT N_("Font Effect")
122 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
123 "text to improve its readability." )
125 #define EFFECT_BACKGROUND 1
126 #define EFFECT_OUTLINE 2
127 #define EFFECT_OUTLINE_FAT 3
129 static int const pi_effects[] = { 1, 2, 3 };
130 static const char *const ppsz_effects_text[] = {
131 N_("Background"),N_("Outline"), N_("Fat Outline") };
132 static const int pi_color_values[] = {
133 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
134 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
135 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
137 static const char *const ppsz_color_descriptions[] = {
138 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
139 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
140 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
143 set_shortname( N_("Text renderer"))
144 set_description( N_("Freetype2 font renderer") )
145 set_category( CAT_VIDEO )
146 set_subcategory( SUBCAT_VIDEO_SUBPIC )
148 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
151 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
152 FONTSIZE_LONGTEXT, true )
154 /* opacity valid on 0..255, with default 255 = fully opaque */
155 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
156 OPACITY_TEXT, OPACITY_LONGTEXT, true )
158 /* hook to the color values list, with default 0x00ffffff = white */
159 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
160 COLOR_LONGTEXT, false )
161 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
163 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
164 FONTSIZER_LONGTEXT, false )
165 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
166 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
167 EFFECT_LONGTEXT, false )
168 change_integer_list( pi_effects, ppsz_effects_text, NULL )
170 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
171 YUVP_LONGTEXT, true )
172 set_capability( "text renderer", 100 )
173 add_shortcut( "text" )
174 set_callbacks( Create, Destroy )
179 /*****************************************************************************
181 *****************************************************************************/
183 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
184 static int RenderText( filter_t *, subpicture_region_t *,
185 subpicture_region_t * );
186 #ifdef HAVE_FONTCONFIG
187 static int RenderHtml( filter_t *, subpicture_region_t *,
188 subpicture_region_t * );
189 static char *FontConfig_Select( FcConfig *, const char *,
194 static int LoadFontsFromAttachments( filter_t *p_filter );
196 static int GetFontSize( filter_t *p_filter );
197 static int SetFontSize( filter_t *, int );
198 static void YUVFromRGB( uint32_t i_argb,
199 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
201 typedef struct line_desc_t line_desc_t;
204 /** NULL-terminated list of glyphs making the string */
205 FT_BitmapGlyph *pp_glyphs;
206 /** list of relative positions for the glyphs */
207 FT_Vector *p_glyph_pos;
208 /** list of RGB information for styled text
209 * -- if the rendering mode supports it (RenderYUVA) and
210 * b_new_color_mode is set, then it becomes possible to
211 * have multicoloured text within the subtitles. */
214 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
215 bool b_new_color_mode;
216 /** underline information -- only supplied if text should be underlined */
217 uint16_t *pi_underline_offset;
218 uint16_t *pi_underline_thickness;
222 int i_red, i_green, i_blue;
227 static line_desc_t *NewLine( int );
232 uint32_t i_font_color; /* ARGB */
233 uint32_t i_karaoke_bg_color; /* ARGB */
240 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
241 static void FreeLines( line_desc_t * );
242 static void FreeLine( line_desc_t * );
244 /*****************************************************************************
245 * filter_sys_t: freetype local data
246 *****************************************************************************
247 * This structure is part of the video output thread descriptor.
248 * It describes the freetype specific properties of an output thread.
249 *****************************************************************************/
252 FT_Library p_library; /* handle to library */
253 FT_Face p_face; /* handle to face object */
255 uint8_t i_font_opacity;
260 int i_default_font_size;
261 int i_display_height;
262 #ifdef HAVE_FONTCONFIG
263 char* psz_fontfamily;
267 input_attachment_t **pp_font_attachments;
268 int i_font_attachments;
272 #define UCHAR uint32_t
273 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
274 #define TR_FONT_STYLE_PTR ft_style_t *
276 #include "text_renderer.h"
278 /*****************************************************************************
279 * Create: allocates osd-text video thread output method
280 *****************************************************************************
281 * This function allocates and initializes a Clone vout method.
282 *****************************************************************************/
283 static int Create( vlc_object_t *p_this )
285 filter_t *p_filter = (filter_t *)p_this;
287 char *psz_fontfile=NULL;
288 char *psz_fontfamily=NULL;
289 int i_error,fontindex;
291 #ifdef HAVE_FONTCONFIG
292 FcPattern *fontpattern = NULL, *fontmatch = NULL;
293 /* Initialise result to Match, as fontconfig doesnt
294 * really set this other than some error-cases */
295 FcResult fontresult = FcResultMatch;
299 /* Allocate structure */
300 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
303 #ifdef HAVE_FONTCONFIG
304 p_sys->psz_fontfamily = NULL;
308 p_sys->p_library = 0;
309 p_sys->i_font_size = 0;
310 p_sys->i_display_height = 0;
312 var_Create( p_filter, "freetype-rel-fontsize",
313 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
315 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
316 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
317 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
318 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
319 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
320 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
321 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
324 if( !psz_fontfamily || !*psz_fontfamily )
326 #ifdef HAVE_FONTCONFIG
327 free( psz_fontfamily);
328 psz_fontfamily=strdup( DEFAULT_FONT );
330 free( psz_fontfamily );
331 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
332 if( !psz_fontfamily )
335 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
336 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
338 strcpy( psz_fontfamily, DEFAULT_FONT );
340 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
344 #ifdef HAVE_FONTCONFIG
345 /* Lets find some fontfile from freetype-font variable family */
347 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
351 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
352 _("Building font cache"),
353 _("Please wait while your font cache is rebuilt.\n"
354 "This should take less than few minutes."), NULL );
356 path = (char *)malloc( PATH_MAX + 1 );
357 /* Fontconfig doesnt seem to know where windows fonts are with
358 * current contribs. So just tell default windows font directory
359 * is the place to search fonts
361 GetWindowsDirectory( path, PATH_MAX + 1 );
362 strcat( path, "\\fonts" );
364 dialog_ProgressSet( p_dialog, NULL, 0.4 );
366 FcConfigAppFontAddDir( NULL , path );
371 dialog_ProgressSet( p_dialog, NULL, 0.5 );
375 msg_Dbg( p_filter, "Building font database.");
377 FcConfigBuildFonts( NULL );
380 msg_Dbg( p_filter, "Finished building font database." );
381 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
383 fontpattern = FcPatternCreate();
387 msg_Err( p_filter, "Creating fontpattern failed");
393 dialog_ProgressSet( p_dialog, NULL, 0.7 );
395 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
396 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
397 free( psz_fontsize );
399 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
401 msg_Err( p_filter, "FontSubstitute failed");
404 FcDefaultSubstitute( fontpattern );
408 dialog_ProgressSet( p_dialog, NULL, 0.8 );
410 /* testing fontresult here doesn't do any good really, but maybe it will
411 * in future as fontconfig code doesn't set it in all cases and just
412 * returns NULL or doesn't set to to Match on all Match cases.*/
413 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
414 if( !fontmatch || fontresult == FcResultNoMatch )
416 msg_Err( p_filter, "Fontmatching failed");
420 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
421 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
424 msg_Err( p_filter, "Failed to get fontfile");
428 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
429 p_sys->psz_fontfamily = strdup( psz_fontfamily );
433 dialog_ProgressSet( p_dialog, NULL, 1.0 );
434 dialog_ProgressDestroy( p_dialog );
439 p_sys->psz_fontfamily = strdup( DEFAULT_FONT )
440 psz_fontfile = psz_fontfamily;
443 i_error = FT_Init_FreeType( &p_sys->p_library );
446 msg_Err( p_filter, "couldn't initialize freetype" );
450 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
451 fontindex, &p_sys->p_face );
453 if( i_error == FT_Err_Unknown_File_Format )
455 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
460 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
464 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
467 msg_Err( p_filter, "font has no unicode translation table" );
471 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
473 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
476 p_sys->pp_font_attachments = NULL;
477 p_sys->i_font_attachments = 0;
479 p_filter->pf_render_text = RenderText;
480 #ifdef HAVE_FONTCONFIG
481 p_filter->pf_render_html = RenderHtml;
482 FcPatternDestroy( fontmatch );
483 FcPatternDestroy( fontpattern );
485 p_filter->pf_render_html = NULL;
488 free( psz_fontfamily );
489 LoadFontsFromAttachments( p_filter );
494 #ifdef HAVE_FONTCONFIG
495 if( fontmatch ) FcPatternDestroy( fontmatch );
496 if( fontpattern ) FcPatternDestroy( fontpattern );
498 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
499 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
500 free( psz_fontfamily );
505 /*****************************************************************************
506 * Destroy: destroy Clone video thread output method
507 *****************************************************************************
508 * Clean up all data and library connections
509 *****************************************************************************/
510 static void Destroy( vlc_object_t *p_this )
512 filter_t *p_filter = (filter_t *)p_this;
513 filter_sys_t *p_sys = p_filter->p_sys;
515 if( p_sys->pp_font_attachments )
519 for( k = 0; k < p_sys->i_font_attachments; k++ )
520 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
522 free( p_sys->pp_font_attachments );
525 #ifdef HAVE_FONTCONFIG
526 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
527 free( p_sys->psz_fontfamily );
530 /* FcFini asserts calling the subfunction FcCacheFini()
531 * even if no other library functions have been made since FcInit(),
532 * so don't call it. */
534 FT_Done_Face( p_sys->p_face );
535 FT_Done_FreeType( p_sys->p_library );
539 /*****************************************************************************
540 * Make any TTF/OTF fonts present in the attachments of the media file
541 * and store them for later use by the FreeType Engine
542 *****************************************************************************/
543 static int LoadFontsFromAttachments( filter_t *p_filter )
545 filter_sys_t *p_sys = p_filter->p_sys;
546 input_thread_t *p_input;
547 input_attachment_t **pp_attachments;
548 int i_attachments_cnt;
550 int rv = VLC_SUCCESS;
552 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
556 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
558 vlc_object_release(p_input);
562 p_sys->i_font_attachments = 0;
563 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
564 if(! p_sys->pp_font_attachments )
567 for( k = 0; k < i_attachments_cnt; k++ )
569 input_attachment_t *p_attach = pp_attachments[k];
571 if( p_sys->pp_font_attachments )
573 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
574 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
575 ( p_attach->i_data > 0 ) &&
576 ( p_attach->p_data != NULL ) )
578 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
582 vlc_input_attachment_Delete( p_attach );
587 vlc_input_attachment_Delete( p_attach );
590 free( pp_attachments );
592 vlc_object_release(p_input);
597 /*****************************************************************************
598 * Render: place string in picture
599 *****************************************************************************
600 * This function merges the previously rendered freetype glyphs into a picture
601 *****************************************************************************/
602 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
603 line_desc_t *p_line, int i_width, int i_height )
605 VLC_UNUSED(p_filter);
606 static const uint8_t pi_gamma[16] =
607 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
608 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
612 int i, x, y, i_pitch;
613 uint8_t i_y; /* YUV values, derived from incoming RGB */
616 /* Create a new subpicture region */
617 memset( &fmt, 0, sizeof(video_format_t) );
618 fmt.i_chroma = VLC_CODEC_YUVP;
620 fmt.i_width = fmt.i_visible_width = i_width + 4;
621 fmt.i_height = fmt.i_visible_height = i_height + 4;
622 if( p_region->fmt.i_visible_width > 0 )
623 fmt.i_visible_width = p_region->fmt.i_visible_width;
624 if( p_region->fmt.i_visible_height > 0 )
625 fmt.i_visible_height = p_region->fmt.i_visible_height;
626 fmt.i_x_offset = fmt.i_y_offset = 0;
628 assert( !p_region->p_picture );
629 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
630 if( !p_region->p_picture )
634 /* Calculate text color components */
635 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
636 25 * p_line->i_blue + 128) >> 8) + 16;
637 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
638 112 * p_line->i_blue + 128) >> 8) + 128;
639 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
640 18 * p_line->i_blue + 128) >> 8) + 128;
643 fmt.p_palette->i_entries = 16;
644 for( i = 0; i < 8; i++ )
646 fmt.p_palette->palette[i][0] = 0;
647 fmt.p_palette->palette[i][1] = 0x80;
648 fmt.p_palette->palette[i][2] = 0x80;
649 fmt.p_palette->palette[i][3] = pi_gamma[i];
650 fmt.p_palette->palette[i][3] =
651 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
653 for( i = 8; i < fmt.p_palette->i_entries; i++ )
655 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
656 fmt.p_palette->palette[i][1] = i_u;
657 fmt.p_palette->palette[i][2] = i_v;
658 fmt.p_palette->palette[i][3] = pi_gamma[i];
659 fmt.p_palette->palette[i][3] =
660 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
663 p_dst = p_region->p_picture->Y_PIXELS;
664 i_pitch = p_region->p_picture->Y_PITCH;
666 /* Initialize the region pixels */
667 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
669 for( ; p_line != NULL; p_line = p_line->p_next )
671 int i_glyph_tmax = 0;
672 int i_bitmap_offset, i_offset, i_align_offset = 0;
673 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
675 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
676 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
679 if( p_line->i_width < i_width )
681 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
683 i_align_offset = i_width - p_line->i_width;
685 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
687 i_align_offset = ( i_width - p_line->i_width ) / 2;
691 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
693 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
695 i_offset = ( p_line->p_glyph_pos[ i ].y +
696 i_glyph_tmax - p_glyph->top + 2 ) *
697 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
700 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
702 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
704 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
706 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
713 /* Outlining (find something better than nearest neighbour filtering ?) */
716 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
717 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
718 uint8_t left, current;
720 for( y = 1; y < (int)fmt.i_height - 1; y++ )
722 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
723 p_dst += p_region->p_picture->Y_PITCH;
726 for( x = 1; x < (int)fmt.i_width - 1; x++ )
729 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
730 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;
734 memset( p_top, 0, fmt.i_width );
740 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
741 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
742 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
743 int i_glyph_tmax, int i_align_offset,
744 uint8_t i_y, uint8_t i_u, uint8_t i_v,
745 subpicture_region_t *p_region)
749 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
751 p_dst_y = p_region->p_picture->Y_PIXELS;
752 p_dst_u = p_region->p_picture->U_PIXELS;
753 p_dst_v = p_region->p_picture->V_PIXELS;
754 p_dst_a = p_region->p_picture->A_PIXELS;
755 i_pitch = p_region->p_picture->A_PITCH;
757 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
758 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
760 for( y = 0; y < i_line_thickness; y++ )
762 int i_extra = p_this_glyph->bitmap.width;
766 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
767 (p_this_glyph_pos->x + p_this_glyph->left);
769 for( x = 0; x < i_extra; x++ )
773 /* break the underline around the tails of any glyphs which cross it */
774 for( z = x - i_line_thickness;
775 z < x + i_line_thickness && b_ok;
778 if( p_next_glyph && ( z >= i_extra ) )
780 int i_row = i_line_offset + p_next_glyph->top + y;
782 if( ( p_next_glyph->bitmap.rows > i_row ) &&
783 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
788 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
790 int i_row = i_line_offset + p_this_glyph->top + y;
792 if( ( p_this_glyph->bitmap.rows > i_row ) &&
793 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
802 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
803 p_dst_u[i_offset+x] = i_u;
804 p_dst_v[i_offset+x] = i_v;
805 p_dst_a[i_offset+x] = 255;
812 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
814 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
815 int i_pitch = p_region->p_picture->A_PITCH;
818 for( ; p_line != NULL; p_line = p_line->p_next )
820 int i_glyph_tmax=0, i = 0;
821 int i_bitmap_offset, i_offset, i_align_offset = 0;
822 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
824 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
825 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
828 if( p_line->i_width < i_width )
830 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
832 i_align_offset = i_width - p_line->i_width;
834 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
836 i_align_offset = ( i_width - p_line->i_width ) / 2;
840 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
842 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
844 i_offset = ( p_line->p_glyph_pos[ i ].y +
845 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
846 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
847 i_align_offset +xoffset;
849 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
851 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
853 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
854 if( p_dst[i_offset+x] <
855 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
857 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
866 /*****************************************************************************
867 * Render: place string in picture
868 *****************************************************************************
869 * This function merges the previously rendered freetype glyphs into a picture
870 *****************************************************************************/
871 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
872 line_desc_t *p_line, int i_width, int i_height )
874 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
876 int i, x, y, i_pitch, i_alpha;
877 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
879 if( i_width == 0 || i_height == 0 )
882 /* Create a new subpicture region */
883 memset( &fmt, 0, sizeof(video_format_t) );
884 fmt.i_chroma = VLC_CODEC_YUVA;
886 fmt.i_width = fmt.i_visible_width = i_width + 6;
887 fmt.i_height = fmt.i_visible_height = i_height + 6;
888 if( p_region->fmt.i_visible_width > 0 )
889 fmt.i_visible_width = p_region->fmt.i_visible_width;
890 if( p_region->fmt.i_visible_height > 0 )
891 fmt.i_visible_height = p_region->fmt.i_visible_height;
892 fmt.i_x_offset = fmt.i_y_offset = 0;
894 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
895 if( !p_region->p_picture )
899 /* Calculate text color components */
900 YUVFromRGB( (p_line->i_red << 16) |
901 (p_line->i_green << 8) |
904 i_alpha = p_line->i_alpha;
906 p_dst_y = p_region->p_picture->Y_PIXELS;
907 p_dst_u = p_region->p_picture->U_PIXELS;
908 p_dst_v = p_region->p_picture->V_PIXELS;
909 p_dst_a = p_region->p_picture->A_PIXELS;
910 i_pitch = p_region->p_picture->A_PITCH;
912 /* Initialize the region pixels */
913 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
915 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
916 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
917 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
918 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
922 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
923 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
924 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
925 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
927 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
928 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
930 DrawBlack( p_line, i_width, p_region, 0, 0);
931 DrawBlack( p_line, i_width, p_region, -1, 0);
932 DrawBlack( p_line, i_width, p_region, 0, -1);
933 DrawBlack( p_line, i_width, p_region, 1, 0);
934 DrawBlack( p_line, i_width, p_region, 0, 1);
937 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
939 DrawBlack( p_line, i_width, p_region, -1, -1);
940 DrawBlack( p_line, i_width, p_region, -1, 1);
941 DrawBlack( p_line, i_width, p_region, 1, -1);
942 DrawBlack( p_line, i_width, p_region, 1, 1);
944 DrawBlack( p_line, i_width, p_region, -2, 0);
945 DrawBlack( p_line, i_width, p_region, 0, -2);
946 DrawBlack( p_line, i_width, p_region, 2, 0);
947 DrawBlack( p_line, i_width, p_region, 0, 2);
949 DrawBlack( p_line, i_width, p_region, -2, -2);
950 DrawBlack( p_line, i_width, p_region, -2, 2);
951 DrawBlack( p_line, i_width, p_region, 2, -2);
952 DrawBlack( p_line, i_width, p_region, 2, 2);
954 DrawBlack( p_line, i_width, p_region, -3, 0);
955 DrawBlack( p_line, i_width, p_region, 0, -3);
956 DrawBlack( p_line, i_width, p_region, 3, 0);
957 DrawBlack( p_line, i_width, p_region, 0, 3);
960 for( ; p_line != NULL; p_line = p_line->p_next )
962 int i_glyph_tmax = 0;
963 int i_bitmap_offset, i_offset, i_align_offset = 0;
964 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
966 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
967 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
970 if( p_line->i_width < i_width )
972 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
974 i_align_offset = i_width - p_line->i_width;
976 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
978 i_align_offset = ( i_width - p_line->i_width ) / 2;
982 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
984 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
986 i_offset = ( p_line->p_glyph_pos[ i ].y +
987 i_glyph_tmax - p_glyph->top + 3 ) *
988 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
991 if( p_line->b_new_color_mode )
993 /* Every glyph can (and in fact must) have its own color */
994 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
997 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
999 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1001 uint8_t i_y_local = i_y;
1002 uint8_t i_u_local = i_u;
1003 uint8_t i_v_local = i_v;
1005 if( p_line->p_fg_bg_ratio != 0x00 )
1007 int i_split = p_glyph->bitmap.width *
1008 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1012 YUVFromRGB( p_line->p_bg_rgb[ i ],
1013 &i_y_local, &i_u_local, &i_v_local );
1017 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1019 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1020 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1022 p_dst_u[i_offset+x] = i_u;
1023 p_dst_v[i_offset+x] = i_v;
1025 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1026 p_dst_a[i_offset+x] = 0xff;
1029 i_offset += i_pitch;
1032 if( p_line->pi_underline_thickness[ i ] )
1034 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1035 p_line->pi_underline_offset[ i ],
1036 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1037 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1038 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1039 i_glyph_tmax, i_align_offset,
1046 /* Apply the alpha setting */
1047 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1048 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1054 * This function renders a text subpicture region into another one.
1055 * It also calculates the size needed for this string, and renders the
1056 * needed glyphs into memory. It is used as pf_add_string callback in
1057 * the vout method by this module
1059 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1060 subpicture_region_t *p_region_in )
1062 filter_sys_t *p_sys = p_filter->p_sys;
1063 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1064 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1065 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1066 int i_string_length;
1068 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1069 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1079 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1080 psz_string = p_region_in->psz_text;
1081 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1083 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1084 i_scale = val.i_int;
1086 if( p_region_in->p_style )
1088 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1089 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1090 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1094 i_font_color = p_sys->i_font_color;
1095 i_font_alpha = 255 - p_sys->i_font_opacity;
1096 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1099 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1100 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1101 SetFontSize( p_filter, i_font_size );
1103 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1104 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1105 i_blue = i_font_color & 0x000000FF;
1107 result.x = result.y = 0;
1108 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1110 psz_unicode = psz_unicode_orig =
1111 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1112 if( psz_unicode == NULL )
1114 #if defined(WORDS_BIGENDIAN)
1115 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1117 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1119 if( iconv_handle == (vlc_iconv_t)-1 )
1121 msg_Warn( p_filter, "unable to do conversion" );
1127 const char *p_in_buffer = psz_string;
1128 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1129 i_in_bytes = strlen( psz_string );
1130 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1131 i_out_bytes_left = i_out_bytes;
1132 p_out_buffer = (char *)psz_unicode;
1133 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1135 &p_out_buffer, &i_out_bytes_left );
1137 vlc_iconv_close( iconv_handle );
1141 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1142 "bytes left %u", (unsigned)i_in_bytes );
1145 *(uint32_t*)p_out_buffer = 0;
1146 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1149 #if defined(HAVE_FRIBIDI)
1151 uint32_t *p_fribidi_string;
1152 int32_t start_pos, pos = 0;
1154 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1155 if( !p_fribidi_string )
1158 /* Do bidi conversion line-by-line */
1159 while( pos < i_string_length )
1161 while( pos < i_string_length )
1163 i_char = psz_unicode[pos];
1164 if (i_char != '\r' && i_char != '\n')
1166 p_fribidi_string[pos] = i_char;
1170 while( pos < i_string_length )
1172 i_char = psz_unicode[pos];
1173 if (i_char == '\r' || i_char == '\n')
1177 if (pos > start_pos)
1179 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1180 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1183 (FriBidiChar*)p_fribidi_string + start_pos,
1188 free( psz_unicode_orig );
1189 psz_unicode = psz_unicode_orig = p_fribidi_string;
1190 p_fribidi_string[ i_string_length ] = 0;
1194 /* Calculate relative glyph positions and a bounding box for the
1196 if( !(p_line = NewLine( strlen( psz_string ))) )
1199 i_pen_x = i_pen_y = 0;
1201 psz_line_start = psz_unicode;
1203 #define face p_sys->p_face
1204 #define glyph face->glyph
1206 while( *psz_unicode )
1208 i_char = *psz_unicode++;
1209 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1214 if( i_char == '\n' )
1216 psz_line_start = psz_unicode;
1217 if( !(p_next = NewLine( strlen( psz_string ))) )
1219 p_line->p_next = p_next;
1220 p_line->i_width = line.xMax;
1221 p_line->i_height = face->size->metrics.height >> 6;
1222 p_line->pp_glyphs[ i ] = NULL;
1223 p_line->i_alpha = i_font_alpha;
1224 p_line->i_red = i_red;
1225 p_line->i_green = i_green;
1226 p_line->i_blue = i_blue;
1229 result.x = __MAX( result.x, line.xMax );
1230 result.y += face->size->metrics.height >> 6;
1233 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1234 i_pen_y += face->size->metrics.height >> 6;
1236 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1241 i_glyph_index = FT_Get_Char_Index( face, i_char );
1242 if( p_sys->i_use_kerning && i_glyph_index
1246 FT_Get_Kerning( face, i_previous, i_glyph_index,
1247 ft_kerning_default, &delta );
1248 i_pen_x += delta.x >> 6;
1251 p_line->p_glyph_pos[ i ].x = i_pen_x;
1252 p_line->p_glyph_pos[ i ].y = i_pen_y;
1253 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1256 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1260 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1263 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1267 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1268 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1271 FT_Done_Glyph( tmp_glyph );
1274 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1277 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1278 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1279 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1281 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1282 p_line->pp_glyphs[ i ] = NULL;
1284 p_line = NewLine( strlen( psz_string ));
1285 if( p_prev ) p_prev->p_next = p_line;
1286 else p_lines = p_line;
1288 uint32_t *psz_unicode_saved = psz_unicode;
1289 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1293 if( psz_unicode == psz_line_start )
1294 { /* try harder to break that line */
1295 psz_unicode = psz_unicode_saved;
1296 while( psz_unicode > psz_line_start &&
1297 *psz_unicode != '_' && *psz_unicode != '/' &&
1298 *psz_unicode != '\\' && *psz_unicode != '.' )
1303 if( psz_unicode == psz_line_start )
1305 msg_Warn( p_filter, "unbreakable string" );
1310 *psz_unicode = '\n';
1312 psz_unicode = psz_line_start;
1315 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1318 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1319 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1321 i_previous = i_glyph_index;
1322 i_pen_x += glyph->advance.x >> 6;
1326 p_line->i_width = line.xMax;
1327 p_line->i_height = face->size->metrics.height >> 6;
1328 p_line->pp_glyphs[ i ] = NULL;
1329 p_line->i_alpha = i_font_alpha;
1330 p_line->i_red = i_red;
1331 p_line->i_green = i_green;
1332 p_line->i_blue = i_blue;
1333 result.x = __MAX( result.x, line.xMax );
1334 result.y += line.yMax - line.yMin;
1339 p_region_out->i_x = p_region_in->i_x;
1340 p_region_out->i_y = p_region_in->i_y;
1342 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1343 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1345 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1347 free( psz_unicode_orig );
1348 FreeLines( p_lines );
1352 free( psz_unicode_orig );
1353 FreeLines( p_lines );
1354 return VLC_EGENERIC;
1357 #ifdef HAVE_FONTCONFIG
1358 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1359 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1360 bool b_italic, bool b_uline )
1362 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1366 p_style->i_font_size = i_font_size;
1367 p_style->i_font_color = i_font_color;
1368 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1369 p_style->b_italic = b_italic;
1370 p_style->b_bold = b_bold;
1371 p_style->b_underline = b_uline;
1373 p_style->psz_fontname = strdup( psz_fontname );
1378 static void DeleteStyle( ft_style_t *p_style )
1382 free( p_style->psz_fontname );
1387 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1394 if(( s1->i_font_size == s2->i_font_size ) &&
1395 ( s1->i_font_color == s2->i_font_color ) &&
1396 ( s1->b_italic == s2->b_italic ) &&
1397 ( s1->b_bold == s2->b_bold ) &&
1398 ( s1->b_underline == s2->b_underline ) &&
1399 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1406 static void IconvText( filter_t *p_filter, const char *psz_string,
1407 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1409 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1411 /* If memory hasn't been allocated for our output string, allocate it here
1412 * - the calling function must now be responsible for freeing it.
1414 if( !*ppsz_unicode )
1415 *ppsz_unicode = (uint32_t *)
1416 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1418 /* We don't need to handle a NULL pointer in *ppsz_unicode
1419 * if we are instead testing for a non NULL value like we are here */
1423 #if defined(WORDS_BIGENDIAN)
1424 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1426 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1428 if( iconv_handle != (vlc_iconv_t)-1 )
1430 char *p_in_buffer, *p_out_buffer;
1431 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1432 i_in_bytes = strlen( psz_string );
1433 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1434 i_out_bytes_left = i_out_bytes;
1435 p_in_buffer = (char *) psz_string;
1436 p_out_buffer = (char *) *ppsz_unicode;
1437 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1438 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1440 vlc_iconv_close( iconv_handle );
1444 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1445 "bytes left %u", (unsigned)i_in_bytes );
1449 *(uint32_t*)p_out_buffer = 0;
1451 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1456 msg_Warn( p_filter, "unable to do conversion" );
1461 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1462 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1465 ft_style_t *p_style = NULL;
1467 char *psz_fontname = NULL;
1468 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1469 uint32_t i_karaoke_bg_color = i_font_color;
1470 int i_font_size = p_sys->i_font_size;
1472 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1473 &i_font_color, &i_karaoke_bg_color ))
1475 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1476 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1481 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1482 bool b_uline, int i_karaoke_bgcolor,
1483 line_desc_t *p_line, uint32_t *psz_unicode,
1484 int *pi_pen_x, int i_pen_y, int *pi_start,
1485 FT_Vector *p_result )
1490 bool b_first_on_line = true;
1493 int i_pen_x_start = *pi_pen_x;
1495 uint32_t *psz_unicode_start = psz_unicode;
1497 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1499 /* Account for part of line already in position */
1500 for( i=0; i<*pi_start; i++ )
1504 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1505 ft_glyph_bbox_pixels, &glyph_size );
1507 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1508 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1509 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1510 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1516 b_first_on_line = false;
1518 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1524 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1525 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1529 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1530 ft_kerning_default, &delta );
1531 *pi_pen_x += delta.x >> 6;
1533 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1534 p_line->p_glyph_pos[ i ].y = i_pen_y;
1536 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1540 "unable to render text FT_Load_Glyph returned %d", i_error );
1541 p_line->pp_glyphs[ i ] = NULL;
1542 return VLC_EGENERIC;
1544 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1548 "unable to render text FT_Get_Glyph returned %d", i_error );
1549 p_line->pp_glyphs[ i ] = NULL;
1550 return VLC_EGENERIC;
1552 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1553 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1556 FT_Done_Glyph( tmp_glyph );
1561 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1562 p_face->size->metrics.y_scale));
1563 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1564 p_face->size->metrics.y_scale));
1566 p_line->pi_underline_offset[ i ] =
1567 ( aOffset < 0 ) ? -aOffset : aOffset;
1568 p_line->pi_underline_thickness[ i ] =
1569 ( aSize < 0 ) ? -aSize : aSize;
1571 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1572 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1573 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1574 p_line->p_fg_bg_ratio[ i ] = 0x00;
1576 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1577 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1578 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1580 for( ; i >= *pi_start; i-- )
1581 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1584 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1588 if( psz_unicode == psz_unicode_start )
1590 if( b_first_on_line )
1592 msg_Warn( p_filter, "unbreakable string" );
1593 p_line->pp_glyphs[ i ] = NULL;
1594 return VLC_EGENERIC;
1596 *pi_pen_x = i_pen_x_start;
1598 p_line->i_width = line.xMax;
1599 p_line->i_height = __MAX( p_line->i_height,
1600 p_face->size->metrics.height >> 6 );
1601 p_line->pp_glyphs[ i ] = NULL;
1603 p_result->x = __MAX( p_result->x, line.xMax );
1604 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1605 i_yMax - i_yMin ) );
1610 *psz_unicode = '\n';
1612 psz_unicode = psz_unicode_start;
1613 *pi_pen_x = i_pen_x_start;
1621 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1622 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1624 i_previous = i_glyph_index;
1625 *pi_pen_x += p_face->glyph->advance.x >> 6;
1628 p_line->i_width = line.xMax;
1629 p_line->i_height = __MAX( p_line->i_height,
1630 p_face->size->metrics.height >> 6 );
1631 p_line->pp_glyphs[ i ] = NULL;
1633 p_result->x = __MAX( p_result->x, line.xMax );
1634 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1635 line.yMax - line.yMin ) );
1639 /* Get rid of any text processed - if necessary repositioning
1640 * at the start of a new line of text
1644 *psz_unicode_start = '\0';
1646 else if( psz_unicode > psz_unicode_start )
1648 for( i=0; psz_unicode[ i ]; i++ )
1649 psz_unicode_start[ i ] = psz_unicode[ i ];
1650 psz_unicode_start[ i ] = '\0';
1656 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1657 uint32_t **psz_text_out, uint32_t *pi_runs,
1658 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1659 ft_style_t *p_style )
1661 uint32_t i_string_length = 0;
1663 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1664 *psz_text_out += i_string_length;
1666 if( ppp_styles && ppi_run_lengths )
1672 *ppp_styles = (ft_style_t **)
1673 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1675 else if( *pi_runs == 1 )
1677 *ppp_styles = (ft_style_t **)
1678 malloc( *pi_runs * sizeof( ft_style_t * ) );
1681 /* We have just malloc'ed this memory successfully -
1682 * *pi_runs HAS to be within the memory area of *ppp_styles */
1685 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1689 if( *ppi_run_lengths )
1691 *ppi_run_lengths = (uint32_t *)
1692 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1694 else if( *pi_runs == 1 )
1696 *ppi_run_lengths = (uint32_t *)
1697 malloc( *pi_runs * sizeof( uint32_t ) );
1700 /* same remarks here */
1701 if( *ppi_run_lengths )
1703 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1706 /* If we couldn't use the p_style argument due to memory allocation
1707 * problems above, release it here.
1709 if( p_style ) DeleteStyle( p_style );
1712 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1716 for( k=0; k < p_sys->i_font_attachments; k++ )
1718 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1720 FT_Face p_face = NULL;
1722 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1730 bool match = !strcasecmp( p_face->family_name,
1731 p_style->psz_fontname );
1733 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1734 match = match && p_style->b_bold;
1736 match = match && !p_style->b_bold;
1738 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1739 match = match && p_style->b_italic;
1741 match = match && !p_style->b_italic;
1749 FT_Done_Face( p_face );
1754 return VLC_EGENERIC;
1757 static int ProcessLines( filter_t *p_filter,
1762 uint32_t *pi_run_lengths,
1763 ft_style_t **pp_styles,
1764 line_desc_t **pp_lines,
1766 FT_Vector *p_result,
1770 uint32_t *pi_k_run_lengths,
1771 uint32_t *pi_k_durations )
1773 filter_sys_t *p_sys = p_filter->p_sys;
1774 ft_style_t **pp_char_styles;
1775 int *p_new_positions = NULL;
1776 int8_t *p_levels = NULL;
1777 uint8_t *pi_karaoke_bar = NULL;
1781 /* Assign each character in the text string its style explicitly, so that
1782 * after the characters have been shuffled around by Fribidi, we can re-apply
1783 * the styles, and to simplify the calculation of runs within a line.
1785 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1786 if( !pp_char_styles )
1791 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1792 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1793 * we just won't be able to display the progress bar; at least we'll
1799 for( j = 0; j < i_runs; j++ )
1800 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1801 pp_char_styles[ i++ ] = pp_styles[ j ];
1803 #if defined(HAVE_FRIBIDI)
1805 ft_style_t **pp_char_styles_new;
1806 int *p_old_positions;
1807 uint32_t *p_fribidi_string;
1808 int start_pos, pos = 0;
1810 pp_char_styles_new = (ft_style_t **)
1811 malloc( i_len * sizeof( ft_style_t * ));
1813 p_fribidi_string = (uint32_t *)
1814 malloc( (i_len + 1) * sizeof(uint32_t) );
1815 p_old_positions = (int *)
1816 malloc( (i_len + 1) * sizeof( int ) );
1817 p_new_positions = (int *)
1818 malloc( (i_len + 1) * sizeof( int ) );
1819 p_levels = (int8_t *)
1820 malloc( (i_len + 1) * sizeof( int8_t ) );
1822 if( ! pp_char_styles_new ||
1823 ! p_fribidi_string ||
1824 ! p_old_positions ||
1825 ! p_new_positions ||
1829 free( p_old_positions );
1830 free( p_new_positions );
1831 free( p_fribidi_string );
1832 free( pp_char_styles_new );
1833 free( pi_karaoke_bar );
1835 free( pp_char_styles );
1839 /* Do bidi conversion line-by-line */
1842 while(pos < i_len) {
1843 if (psz_text[pos] != '\n')
1845 p_fribidi_string[pos] = psz_text[pos];
1846 pp_char_styles_new[pos] = pp_char_styles[pos];
1847 p_new_positions[pos] = pos;
1852 while(pos < i_len) {
1853 if (psz_text[pos] == '\n')
1857 if (pos > start_pos)
1859 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1860 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1861 pos - start_pos, &base_dir,
1862 (FriBidiChar*)p_fribidi_string + start_pos,
1863 p_new_positions + start_pos,
1865 p_levels + start_pos );
1866 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1868 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1869 p_old_positions[ j - start_pos ] ];
1870 p_new_positions[ j ] += start_pos;
1874 free( p_old_positions );
1875 free( pp_char_styles );
1876 pp_char_styles = pp_char_styles_new;
1877 psz_text = p_fribidi_string;
1878 p_fribidi_string[ i_len ] = 0;
1881 /* Work out the karaoke */
1882 if( pi_karaoke_bar )
1884 int64_t i_last_duration = 0;
1885 int64_t i_duration = 0;
1886 int64_t i_start_pos = 0;
1887 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1889 for( k = 0; k< i_k_runs; k++ )
1891 double fraction = 0.0;
1893 i_duration += pi_k_durations[ k ];
1895 if( i_duration < i_elapsed )
1897 /* Completely finished this run-length -
1898 * let it render normally */
1902 else if( i_elapsed < i_last_duration )
1904 /* Haven't got up to this segment yet -
1905 * render it completely in karaoke BG mode */
1911 /* Partway through this run */
1913 fraction = (double)(i_elapsed - i_last_duration) /
1914 (double)pi_k_durations[ k ];
1916 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1918 double shade = pi_k_run_lengths[ k ] * fraction;
1920 if( p_new_positions )
1921 j = p_new_positions[ i_start_pos + i ];
1923 j = i_start_pos + i;
1925 if( i < (uint32_t)shade )
1926 pi_karaoke_bar[ j ] = 0xff;
1927 else if( (double)i > shade )
1928 pi_karaoke_bar[ j ] = 0x00;
1931 shade -= (int)shade;
1932 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1933 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1937 i_last_duration = i_duration;
1938 i_start_pos += pi_k_run_lengths[ k ];
1942 free( p_new_positions );
1944 FT_Vector tmp_result;
1946 line_desc_t *p_line = NULL;
1947 line_desc_t *p_prev = NULL;
1953 p_result->x = p_result->y = 0;
1954 tmp_result.x = tmp_result.y = 0;
1957 for( k = 0; k <= (uint32_t) i_len; k++ )
1959 if( ( k == (uint32_t) i_len ) ||
1961 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1963 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1965 /* End of the current style run */
1966 FT_Face p_face = NULL;
1969 /* Look for a match amongst our attachments first */
1970 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1974 char *psz_fontfile = NULL;
1976 psz_fontfile = FontConfig_Select( NULL,
1977 p_style->psz_fontname,
1981 if( psz_fontfile && ! *psz_fontfile )
1983 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1984 " so using default font", p_style->psz_fontname,
1985 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1986 (p_style->b_bold ? "(Bold)" :
1987 (p_style->b_italic ? "(Italic)" : ""))) );
1988 free( psz_fontfile );
1989 psz_fontfile = NULL;
1994 if( FT_New_Face( p_sys->p_library,
1995 psz_fontfile, i_idx, &p_face ) )
1997 free( psz_fontfile );
1998 free( pp_char_styles );
1999 #if defined(HAVE_FRIBIDI)
2002 free( pi_karaoke_bar );
2003 return VLC_EGENERIC;
2005 free( psz_fontfile );
2009 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2011 /* We've loaded a font face which is unhelpful for actually
2012 * rendering text - fallback to the default one.
2014 FT_Done_Face( p_face );
2018 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2019 ft_encoding_unicode ) ||
2020 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2021 p_style->i_font_size ) )
2023 if( p_face ) FT_Done_Face( p_face );
2024 free( pp_char_styles );
2025 #if defined(HAVE_FRIBIDI)
2028 free( pi_karaoke_bar );
2029 return VLC_EGENERIC;
2031 p_sys->i_use_kerning =
2032 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2035 uint32_t *psz_unicode = (uint32_t *)
2036 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2039 if( p_face ) FT_Done_Face( p_face );
2040 free( pp_char_styles );
2041 free( psz_unicode );
2042 #if defined(HAVE_FRIBIDI)
2045 free( pi_karaoke_bar );
2048 memcpy( psz_unicode, psz_text + i_prev,
2049 sizeof( uint32_t ) * ( k - i_prev ) );
2050 psz_unicode[ k - i_prev ] = 0;
2051 while( *psz_unicode )
2055 if( !(p_line = NewLine( i_len - i_prev)) )
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 /* New Color mode only works in YUVA rendering mode --
2067 * (RGB mode has palette constraints on it). We therefore
2068 * need to populate the legacy colour fields also.
2070 p_line->b_new_color_mode = true;
2071 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2072 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2073 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2074 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2075 p_line->p_next = NULL;
2077 i_pen_y += tmp_result.y;
2081 if( p_prev ) p_prev->p_next = p_line;
2082 else *pp_lines = p_line;
2085 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2086 p_style->i_font_color, p_style->b_underline,
2087 p_style->i_karaoke_bg_color,
2088 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2089 &tmp_result ) != VLC_SUCCESS )
2091 if( p_face ) FT_Done_Face( p_face );
2092 free( pp_char_styles );
2093 free( psz_unicode );
2094 #if defined(HAVE_FRIBIDI)
2097 free( pi_karaoke_bar );
2098 return VLC_EGENERIC;
2103 p_result->x = __MAX( p_result->x, tmp_result.x );
2104 p_result->y += tmp_result.y;
2109 if( *psz_unicode == '\n')
2113 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2115 *c_ptr = *(c_ptr+1);
2120 free( psz_unicode );
2121 if( p_face ) FT_Done_Face( p_face );
2125 free( pp_char_styles );
2126 #if defined(HAVE_FRIBIDI)
2131 p_result->x = __MAX( p_result->x, tmp_result.x );
2132 p_result->y += tmp_result.y;
2135 if( pi_karaoke_bar )
2138 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2140 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2142 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2146 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2148 /* 100% BG colour will render faster if we
2149 * instead make it 100% FG colour, so leave
2150 * the ratio alone and copy the value across
2152 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2156 if( pi_karaoke_bar[ i ] & 0x80 )
2158 /* Swap Left and Right sides over for Right aligned
2159 * language text (eg. Arabic, Hebrew)
2161 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2163 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2164 p_line->p_bg_rgb[ k ] = i_tmp;
2166 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2169 /* Jump over the '\n' at the line-end */
2172 free( pi_karaoke_bar );
2178 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2179 subpicture_region_t *p_region_in )
2181 int rv = VLC_SUCCESS;
2182 stream_t *p_sub = NULL;
2183 xml_reader_t *p_xml_reader = NULL;
2185 if( !p_region_in || !p_region_in->psz_html )
2186 return VLC_EGENERIC;
2188 /* Reset the default fontsize in case screen metrics have changed */
2189 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2191 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2192 (uint8_t *) p_region_in->psz_html,
2193 strlen( p_region_in->psz_html ),
2197 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2198 if( p_filter->p_sys->p_xml )
2200 bool b_karaoke = false;
2202 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2205 /* Look for Root Node */
2206 if( xml_ReaderRead( p_xml_reader ) == 1 )
2208 char *psz_node = xml_ReaderName( p_xml_reader );
2210 if( !strcasecmp( "karaoke", psz_node ) )
2212 /* We're going to have to render the text a number
2213 * of times to show the progress marker on the text.
2215 var_SetBool( p_filter, "text-rerender", true );
2218 else if( !strcasecmp( "text", psz_node ) )
2224 /* Only text and karaoke tags are supported */
2225 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2226 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2227 p_xml_reader = NULL;
2239 uint32_t i_runs = 0;
2240 uint32_t i_k_runs = 0;
2241 uint32_t *pi_run_lengths = NULL;
2242 uint32_t *pi_k_run_lengths = NULL;
2243 uint32_t *pi_k_durations = NULL;
2244 ft_style_t **pp_styles = NULL;
2246 line_desc_t *p_lines = NULL;
2248 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2249 sizeof( uint32_t ) );
2254 rv = ProcessNodes( p_filter, p_xml_reader,
2255 p_region_in->p_style, psz_text, &i_len,
2256 &i_runs, &pi_run_lengths, &pp_styles,
2258 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2261 p_region_out->i_x = p_region_in->i_x;
2262 p_region_out->i_y = p_region_in->i_y;
2264 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2266 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2267 pi_run_lengths, pp_styles, &p_lines, &result,
2268 b_karaoke, i_k_runs, pi_k_run_lengths,
2272 for( k=0; k<i_runs; k++)
2273 DeleteStyle( pp_styles[k] );
2275 free( pi_run_lengths );
2278 /* Don't attempt to render text that couldn't be layed out
2281 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2283 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2285 Render( p_filter, p_region_out, p_lines,
2286 result.x, result.y );
2290 RenderYUVA( p_filter, p_region_out, p_lines,
2291 result.x, result.y );
2295 FreeLines( p_lines );
2297 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2300 stream_Delete( p_sub );
2306 static char* FontConfig_Select( FcConfig* priv, const char* family,
2307 bool b_bold, bool b_italic, int *i_idx )
2310 FcPattern *pat, *p_pat;
2314 pat = FcPatternCreate();
2315 if (!pat) return NULL;
2317 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2318 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2319 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2320 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2322 FcDefaultSubstitute( pat );
2324 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2326 FcPatternDestroy( pat );
2330 p_pat = FcFontMatch( priv, pat, &result );
2331 FcPatternDestroy( pat );
2332 if( !p_pat ) return NULL;
2334 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2335 || ( val_b != FcTrue ) )
2337 FcPatternDestroy( p_pat );
2340 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2345 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2347 FcPatternDestroy( p_pat );
2352 if( strcasecmp((const char*)val_s, family ) != 0 )
2353 msg_Warn( p_filter, "fontconfig: selected font family is not"
2354 "the requested one: '%s' != '%s'\n",
2355 (const char*)val_s, family );
2358 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2360 FcPatternDestroy( p_pat );
2364 FcPatternDestroy( p_pat );
2365 return strdup( (const char*)val_s );
2369 static void FreeLine( line_desc_t *p_line )
2372 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2374 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2376 free( p_line->pp_glyphs );
2377 free( p_line->p_glyph_pos );
2378 free( p_line->p_fg_rgb );
2379 free( p_line->p_bg_rgb );
2380 free( p_line->p_fg_bg_ratio );
2381 free( p_line->pi_underline_offset );
2382 free( p_line->pi_underline_thickness );
2386 static void FreeLines( line_desc_t *p_lines )
2388 line_desc_t *p_line, *p_next;
2390 if( !p_lines ) return;
2392 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2394 p_next = p_line->p_next;
2399 static line_desc_t *NewLine( int i_count )
2401 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2403 if( !p_line ) return NULL;
2404 p_line->i_height = 0;
2405 p_line->i_width = 0;
2406 p_line->p_next = NULL;
2408 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2409 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2410 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2411 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2412 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2413 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2414 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2415 if( ( p_line->pp_glyphs == NULL ) ||
2416 ( p_line->p_glyph_pos == NULL ) ||
2417 ( p_line->p_fg_rgb == NULL ) ||
2418 ( p_line->p_bg_rgb == NULL ) ||
2419 ( p_line->p_fg_bg_ratio == NULL ) ||
2420 ( p_line->pi_underline_offset == NULL ) ||
2421 ( p_line->pi_underline_thickness == NULL ) )
2423 free( p_line->pi_underline_thickness );
2424 free( p_line->pi_underline_offset );
2425 free( p_line->p_fg_rgb );
2426 free( p_line->p_bg_rgb );
2427 free( p_line->p_fg_bg_ratio );
2428 free( p_line->p_glyph_pos );
2429 free( p_line->pp_glyphs );
2433 p_line->pp_glyphs[0] = NULL;
2434 p_line->b_new_color_mode = false;
2439 static int GetFontSize( filter_t *p_filter )
2441 filter_sys_t *p_sys = p_filter->p_sys;
2445 if( p_sys->i_default_font_size )
2447 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2448 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2450 i_size = p_sys->i_default_font_size;
2454 var_Get( p_filter, "freetype-rel-fontsize", &val );
2457 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2458 p_filter->p_sys->i_display_height =
2459 p_filter->fmt_out.video.i_height;
2464 msg_Warn( p_filter, "invalid fontsize, using 12" );
2465 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2466 i_size = 12 * val.i_int / 1000;
2473 static int SetFontSize( filter_t *p_filter, int i_size )
2475 filter_sys_t *p_sys = p_filter->p_sys;
2479 i_size = GetFontSize( p_filter );
2481 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2484 p_sys->i_font_size = i_size;
2486 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2488 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2489 return VLC_EGENERIC;
2495 static void YUVFromRGB( uint32_t i_argb,
2496 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2498 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2499 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2500 int i_blue = ( i_argb & 0x000000ff );
2502 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2503 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2504 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2505 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2506 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2507 -585 * i_blue + 4096 + 1048576) >> 13, 240);