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 static const uint8_t pi_gamma[16] =
606 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
607 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
611 int i, x, y, i_pitch;
612 uint8_t i_y; /* YUV values, derived from incoming RGB */
615 /* Create a new subpicture region */
616 memset( &fmt, 0, sizeof(video_format_t) );
617 fmt.i_chroma = VLC_CODEC_YUVP;
619 fmt.i_width = fmt.i_visible_width = i_width + 4;
620 fmt.i_height = fmt.i_visible_height = i_height + 4;
621 if( p_region->fmt.i_visible_width > 0 )
622 fmt.i_visible_width = p_region->fmt.i_visible_width;
623 if( p_region->fmt.i_visible_height > 0 )
624 fmt.i_visible_height = p_region->fmt.i_visible_height;
625 fmt.i_x_offset = fmt.i_y_offset = 0;
627 assert( !p_region->p_picture );
628 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
629 if( !p_region->p_picture )
633 /* Calculate text color components */
634 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
635 25 * p_line->i_blue + 128) >> 8) + 16;
636 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
637 112 * p_line->i_blue + 128) >> 8) + 128;
638 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
639 18 * p_line->i_blue + 128) >> 8) + 128;
642 fmt.p_palette->i_entries = 16;
643 for( i = 0; i < 8; i++ )
645 fmt.p_palette->palette[i][0] = 0;
646 fmt.p_palette->palette[i][1] = 0x80;
647 fmt.p_palette->palette[i][2] = 0x80;
648 fmt.p_palette->palette[i][3] = pi_gamma[i];
649 fmt.p_palette->palette[i][3] =
650 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
652 for( i = 8; i < fmt.p_palette->i_entries; i++ )
654 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
655 fmt.p_palette->palette[i][1] = i_u;
656 fmt.p_palette->palette[i][2] = i_v;
657 fmt.p_palette->palette[i][3] = pi_gamma[i];
658 fmt.p_palette->palette[i][3] =
659 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
662 p_dst = p_region->p_picture->Y_PIXELS;
663 i_pitch = p_region->p_picture->Y_PITCH;
665 /* Initialize the region pixels */
666 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
668 for( ; p_line != NULL; p_line = p_line->p_next )
670 int i_glyph_tmax = 0;
671 int i_bitmap_offset, i_offset, i_align_offset = 0;
672 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
674 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
675 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
678 if( p_line->i_width < i_width )
680 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
682 i_align_offset = i_width - p_line->i_width;
684 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
686 i_align_offset = ( i_width - p_line->i_width ) / 2;
690 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
692 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
694 i_offset = ( p_line->p_glyph_pos[ i ].y +
695 i_glyph_tmax - p_glyph->top + 2 ) *
696 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
699 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
701 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
703 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
705 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
712 /* Outlining (find something better than nearest neighbour filtering ?) */
715 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
716 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
717 uint8_t left, current;
719 for( y = 1; y < (int)fmt.i_height - 1; y++ )
721 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
722 p_dst += p_region->p_picture->Y_PITCH;
725 for( x = 1; x < (int)fmt.i_width - 1; x++ )
728 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
729 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;
733 memset( p_top, 0, fmt.i_width );
739 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
740 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
741 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
742 int i_glyph_tmax, int i_align_offset,
743 uint8_t i_y, uint8_t i_u, uint8_t i_v,
744 subpicture_region_t *p_region)
748 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
750 p_dst_y = p_region->p_picture->Y_PIXELS;
751 p_dst_u = p_region->p_picture->U_PIXELS;
752 p_dst_v = p_region->p_picture->V_PIXELS;
753 p_dst_a = p_region->p_picture->A_PIXELS;
754 i_pitch = p_region->p_picture->A_PITCH;
756 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
757 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
759 for( y = 0; y < i_line_thickness; y++ )
761 int i_extra = p_this_glyph->bitmap.width;
765 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
766 (p_this_glyph_pos->x + p_this_glyph->left);
768 for( x = 0; x < i_extra; x++ )
772 /* break the underline around the tails of any glyphs which cross it */
773 for( z = x - i_line_thickness;
774 z < x + i_line_thickness && b_ok;
777 if( p_next_glyph && ( z >= i_extra ) )
779 int i_row = i_line_offset + p_next_glyph->top + y;
781 if( ( p_next_glyph->bitmap.rows > i_row ) &&
782 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
787 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
789 int i_row = i_line_offset + p_this_glyph->top + y;
791 if( ( p_this_glyph->bitmap.rows > i_row ) &&
792 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
801 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
802 p_dst_u[i_offset+x] = i_u;
803 p_dst_v[i_offset+x] = i_v;
804 p_dst_a[i_offset+x] = 255;
811 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
813 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
814 int i_pitch = p_region->p_picture->A_PITCH;
817 for( ; p_line != NULL; p_line = p_line->p_next )
819 int i_glyph_tmax=0, i = 0;
820 int i_bitmap_offset, i_offset, i_align_offset = 0;
821 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
823 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
824 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
827 if( p_line->i_width < i_width )
829 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
831 i_align_offset = i_width - p_line->i_width;
833 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
835 i_align_offset = ( i_width - p_line->i_width ) / 2;
839 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
841 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
843 i_offset = ( p_line->p_glyph_pos[ i ].y +
844 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
845 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
846 i_align_offset +xoffset;
848 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
850 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
852 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
853 if( p_dst[i_offset+x] <
854 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
856 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
865 /*****************************************************************************
866 * Render: place string in picture
867 *****************************************************************************
868 * This function merges the previously rendered freetype glyphs into a picture
869 *****************************************************************************/
870 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
871 line_desc_t *p_line, int i_width, int i_height )
873 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
875 int i, x, y, i_pitch, i_alpha;
876 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
878 if( i_width == 0 || i_height == 0 )
881 /* Create a new subpicture region */
882 memset( &fmt, 0, sizeof(video_format_t) );
883 fmt.i_chroma = VLC_CODEC_YUVA;
885 fmt.i_width = fmt.i_visible_width = i_width + 6;
886 fmt.i_height = fmt.i_visible_height = i_height + 6;
887 if( p_region->fmt.i_visible_width > 0 )
888 fmt.i_visible_width = p_region->fmt.i_visible_width;
889 if( p_region->fmt.i_visible_height > 0 )
890 fmt.i_visible_height = p_region->fmt.i_visible_height;
891 fmt.i_x_offset = fmt.i_y_offset = 0;
893 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
894 if( !p_region->p_picture )
898 /* Calculate text color components */
899 YUVFromRGB( (p_line->i_red << 16) |
900 (p_line->i_green << 8) |
903 i_alpha = p_line->i_alpha;
905 p_dst_y = p_region->p_picture->Y_PIXELS;
906 p_dst_u = p_region->p_picture->U_PIXELS;
907 p_dst_v = p_region->p_picture->V_PIXELS;
908 p_dst_a = p_region->p_picture->A_PIXELS;
909 i_pitch = p_region->p_picture->A_PITCH;
911 /* Initialize the region pixels */
912 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
914 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
915 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
916 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
917 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
921 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
922 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
923 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
924 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
926 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
927 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
929 DrawBlack( p_line, i_width, p_region, 0, 0);
930 DrawBlack( p_line, i_width, p_region, -1, 0);
931 DrawBlack( p_line, i_width, p_region, 0, -1);
932 DrawBlack( p_line, i_width, p_region, 1, 0);
933 DrawBlack( p_line, i_width, p_region, 0, 1);
936 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
938 DrawBlack( p_line, i_width, p_region, -1, -1);
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);
943 DrawBlack( p_line, i_width, p_region, -2, 0);
944 DrawBlack( p_line, i_width, p_region, 0, -2);
945 DrawBlack( p_line, i_width, p_region, 2, 0);
946 DrawBlack( p_line, i_width, p_region, 0, 2);
948 DrawBlack( p_line, i_width, p_region, -2, -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);
953 DrawBlack( p_line, i_width, p_region, -3, 0);
954 DrawBlack( p_line, i_width, p_region, 0, -3);
955 DrawBlack( p_line, i_width, p_region, 3, 0);
956 DrawBlack( p_line, i_width, p_region, 0, 3);
959 for( ; p_line != NULL; p_line = p_line->p_next )
961 int i_glyph_tmax = 0;
962 int i_bitmap_offset, i_offset, i_align_offset = 0;
963 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
965 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
966 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
969 if( p_line->i_width < i_width )
971 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
973 i_align_offset = i_width - p_line->i_width;
975 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
977 i_align_offset = ( i_width - p_line->i_width ) / 2;
981 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
983 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
985 i_offset = ( p_line->p_glyph_pos[ i ].y +
986 i_glyph_tmax - p_glyph->top + 3 ) *
987 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
990 if( p_line->b_new_color_mode )
992 /* Every glyph can (and in fact must) have its own color */
993 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
996 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
998 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1000 uint8_t i_y_local = i_y;
1001 uint8_t i_u_local = i_u;
1002 uint8_t i_v_local = i_v;
1004 if( p_line->p_fg_bg_ratio != 0x00 )
1006 int i_split = p_glyph->bitmap.width *
1007 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1011 YUVFromRGB( p_line->p_bg_rgb[ i ],
1012 &i_y_local, &i_u_local, &i_v_local );
1016 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1018 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1019 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1021 p_dst_u[i_offset+x] = i_u;
1022 p_dst_v[i_offset+x] = i_v;
1024 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1025 p_dst_a[i_offset+x] = 0xff;
1028 i_offset += i_pitch;
1031 if( p_line->pi_underline_thickness[ i ] )
1033 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1034 p_line->pi_underline_offset[ i ],
1035 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1036 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1037 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1038 i_glyph_tmax, i_align_offset,
1045 /* Apply the alpha setting */
1046 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1047 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1053 * This function renders a text subpicture region into another one.
1054 * It also calculates the size needed for this string, and renders the
1055 * needed glyphs into memory. It is used as pf_add_string callback in
1056 * the vout method by this module
1058 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1059 subpicture_region_t *p_region_in )
1061 filter_sys_t *p_sys = p_filter->p_sys;
1062 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1063 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1064 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1065 int i_string_length;
1067 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1068 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1078 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1079 psz_string = p_region_in->psz_text;
1080 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1082 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1083 i_scale = val.i_int;
1085 if( p_region_in->p_style )
1087 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1088 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1089 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1093 i_font_color = p_sys->i_font_color;
1094 i_font_alpha = 255 - p_sys->i_font_opacity;
1095 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1098 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1099 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1100 SetFontSize( p_filter, i_font_size );
1102 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1103 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1104 i_blue = i_font_color & 0x000000FF;
1106 result.x = result.y = 0;
1107 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1109 psz_unicode = psz_unicode_orig =
1110 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1111 if( psz_unicode == NULL )
1113 #if defined(WORDS_BIGENDIAN)
1114 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1116 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1118 if( iconv_handle == (vlc_iconv_t)-1 )
1120 msg_Warn( p_filter, "unable to do conversion" );
1126 const char *p_in_buffer = psz_string;
1127 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1128 i_in_bytes = strlen( psz_string );
1129 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1130 i_out_bytes_left = i_out_bytes;
1131 p_out_buffer = (char *)psz_unicode;
1132 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1134 &p_out_buffer, &i_out_bytes_left );
1136 vlc_iconv_close( iconv_handle );
1140 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1141 "bytes left %u", (unsigned)i_in_bytes );
1144 *(uint32_t*)p_out_buffer = 0;
1145 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1148 #if defined(HAVE_FRIBIDI)
1150 uint32_t *p_fribidi_string;
1151 int32_t start_pos, pos = 0;
1153 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1154 if( !p_fribidi_string )
1157 /* Do bidi conversion line-by-line */
1158 while( pos < i_string_length )
1160 while( pos < i_string_length )
1162 i_char = psz_unicode[pos];
1163 if (i_char != '\r' && i_char != '\n')
1165 p_fribidi_string[pos] = i_char;
1169 while( pos < i_string_length )
1171 i_char = psz_unicode[pos];
1172 if (i_char == '\r' || i_char == '\n')
1176 if (pos > start_pos)
1178 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1179 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1182 (FriBidiChar*)p_fribidi_string + start_pos,
1187 free( psz_unicode_orig );
1188 psz_unicode = psz_unicode_orig = p_fribidi_string;
1189 p_fribidi_string[ i_string_length ] = 0;
1193 /* Calculate relative glyph positions and a bounding box for the
1195 if( !(p_line = NewLine( strlen( psz_string ))) )
1198 i_pen_x = i_pen_y = 0;
1200 psz_line_start = psz_unicode;
1202 #define face p_sys->p_face
1203 #define glyph face->glyph
1205 while( *psz_unicode )
1207 i_char = *psz_unicode++;
1208 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1213 if( i_char == '\n' )
1215 psz_line_start = psz_unicode;
1216 if( !(p_next = NewLine( strlen( psz_string ))) )
1218 p_line->p_next = p_next;
1219 p_line->i_width = line.xMax;
1220 p_line->i_height = face->size->metrics.height >> 6;
1221 p_line->pp_glyphs[ i ] = NULL;
1222 p_line->i_alpha = i_font_alpha;
1223 p_line->i_red = i_red;
1224 p_line->i_green = i_green;
1225 p_line->i_blue = i_blue;
1228 result.x = __MAX( result.x, line.xMax );
1229 result.y += face->size->metrics.height >> 6;
1232 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1233 i_pen_y += face->size->metrics.height >> 6;
1235 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1240 i_glyph_index = FT_Get_Char_Index( face, i_char );
1241 if( p_sys->i_use_kerning && i_glyph_index
1245 FT_Get_Kerning( face, i_previous, i_glyph_index,
1246 ft_kerning_default, &delta );
1247 i_pen_x += delta.x >> 6;
1250 p_line->p_glyph_pos[ i ].x = i_pen_x;
1251 p_line->p_glyph_pos[ i ].y = i_pen_y;
1252 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1255 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1259 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1262 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1266 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1267 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1270 FT_Done_Glyph( tmp_glyph );
1273 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1276 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1277 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1278 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1280 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1281 p_line->pp_glyphs[ i ] = NULL;
1283 p_line = NewLine( strlen( psz_string ));
1284 if( p_prev ) p_prev->p_next = p_line;
1285 else p_lines = p_line;
1287 uint32_t *psz_unicode_saved = psz_unicode;
1288 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1292 if( psz_unicode == psz_line_start )
1293 { /* try harder to break that line */
1294 psz_unicode = psz_unicode_saved;
1295 while( psz_unicode > psz_line_start &&
1296 *psz_unicode != '_' && *psz_unicode != '/' &&
1297 *psz_unicode != '\\' && *psz_unicode != '.' )
1302 if( psz_unicode == psz_line_start )
1304 msg_Warn( p_filter, "unbreakable string" );
1309 *psz_unicode = '\n';
1311 psz_unicode = psz_line_start;
1314 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1317 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1318 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1320 i_previous = i_glyph_index;
1321 i_pen_x += glyph->advance.x >> 6;
1325 p_line->i_width = line.xMax;
1326 p_line->i_height = face->size->metrics.height >> 6;
1327 p_line->pp_glyphs[ i ] = NULL;
1328 p_line->i_alpha = i_font_alpha;
1329 p_line->i_red = i_red;
1330 p_line->i_green = i_green;
1331 p_line->i_blue = i_blue;
1332 result.x = __MAX( result.x, line.xMax );
1333 result.y += line.yMax - line.yMin;
1338 p_region_out->i_x = p_region_in->i_x;
1339 p_region_out->i_y = p_region_in->i_y;
1341 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1342 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1344 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1346 free( psz_unicode_orig );
1347 FreeLines( p_lines );
1351 free( psz_unicode_orig );
1352 FreeLines( p_lines );
1353 return VLC_EGENERIC;
1356 #ifdef HAVE_FONTCONFIG
1357 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1358 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1359 bool b_italic, bool b_uline )
1361 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1365 p_style->i_font_size = i_font_size;
1366 p_style->i_font_color = i_font_color;
1367 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1368 p_style->b_italic = b_italic;
1369 p_style->b_bold = b_bold;
1370 p_style->b_underline = b_uline;
1372 p_style->psz_fontname = strdup( psz_fontname );
1377 static void DeleteStyle( ft_style_t *p_style )
1381 free( p_style->psz_fontname );
1386 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1393 if(( s1->i_font_size == s2->i_font_size ) &&
1394 ( s1->i_font_color == s2->i_font_color ) &&
1395 ( s1->b_italic == s2->b_italic ) &&
1396 ( s1->b_bold == s2->b_bold ) &&
1397 ( s1->b_underline == s2->b_underline ) &&
1398 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1405 static void IconvText( filter_t *p_filter, const char *psz_string,
1406 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1408 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1410 /* If memory hasn't been allocated for our output string, allocate it here
1411 * - the calling function must now be responsible for freeing it.
1413 if( !*ppsz_unicode )
1414 *ppsz_unicode = (uint32_t *)
1415 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1417 /* We don't need to handle a NULL pointer in *ppsz_unicode
1418 * if we are instead testing for a non NULL value like we are here */
1422 #if defined(WORDS_BIGENDIAN)
1423 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1425 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1427 if( iconv_handle != (vlc_iconv_t)-1 )
1429 char *p_in_buffer, *p_out_buffer;
1430 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1431 i_in_bytes = strlen( psz_string );
1432 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1433 i_out_bytes_left = i_out_bytes;
1434 p_in_buffer = (char *) psz_string;
1435 p_out_buffer = (char *) *ppsz_unicode;
1436 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1437 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1439 vlc_iconv_close( iconv_handle );
1443 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1444 "bytes left %u", (unsigned)i_in_bytes );
1448 *(uint32_t*)p_out_buffer = 0;
1450 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1455 msg_Warn( p_filter, "unable to do conversion" );
1460 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1461 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1464 ft_style_t *p_style = NULL;
1466 char *psz_fontname = NULL;
1467 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1468 uint32_t i_karaoke_bg_color = i_font_color;
1469 int i_font_size = p_sys->i_font_size;
1471 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1472 &i_font_color, &i_karaoke_bg_color ))
1474 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1475 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1480 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1481 bool b_uline, int i_karaoke_bgcolor,
1482 line_desc_t *p_line, uint32_t *psz_unicode,
1483 int *pi_pen_x, int i_pen_y, int *pi_start,
1484 FT_Vector *p_result )
1489 bool b_first_on_line = true;
1492 int i_pen_x_start = *pi_pen_x;
1494 uint32_t *psz_unicode_start = psz_unicode;
1496 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1498 /* Account for part of line already in position */
1499 for( i=0; i<*pi_start; i++ )
1503 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1504 ft_glyph_bbox_pixels, &glyph_size );
1506 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1507 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1508 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1509 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1515 b_first_on_line = false;
1517 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1523 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1524 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1528 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1529 ft_kerning_default, &delta );
1530 *pi_pen_x += delta.x >> 6;
1532 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1533 p_line->p_glyph_pos[ i ].y = i_pen_y;
1535 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1539 "unable to render text FT_Load_Glyph returned %d", i_error );
1540 p_line->pp_glyphs[ i ] = NULL;
1541 return VLC_EGENERIC;
1543 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1547 "unable to render text FT_Get_Glyph returned %d", i_error );
1548 p_line->pp_glyphs[ i ] = NULL;
1549 return VLC_EGENERIC;
1551 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1552 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1555 FT_Done_Glyph( tmp_glyph );
1560 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1561 p_face->size->metrics.y_scale));
1562 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1563 p_face->size->metrics.y_scale));
1565 p_line->pi_underline_offset[ i ] =
1566 ( aOffset < 0 ) ? -aOffset : aOffset;
1567 p_line->pi_underline_thickness[ i ] =
1568 ( aSize < 0 ) ? -aSize : aSize;
1570 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1571 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1572 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1573 p_line->p_fg_bg_ratio[ i ] = 0x00;
1575 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1576 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1577 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1579 for( ; i >= *pi_start; i-- )
1580 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1583 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1587 if( psz_unicode == psz_unicode_start )
1589 if( b_first_on_line )
1591 msg_Warn( p_filter, "unbreakable string" );
1592 p_line->pp_glyphs[ i ] = NULL;
1593 return VLC_EGENERIC;
1595 *pi_pen_x = i_pen_x_start;
1597 p_line->i_width = line.xMax;
1598 p_line->i_height = __MAX( p_line->i_height,
1599 p_face->size->metrics.height >> 6 );
1600 p_line->pp_glyphs[ i ] = NULL;
1602 p_result->x = __MAX( p_result->x, line.xMax );
1603 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1604 i_yMax - i_yMin ) );
1609 *psz_unicode = '\n';
1611 psz_unicode = psz_unicode_start;
1612 *pi_pen_x = i_pen_x_start;
1620 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1621 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1623 i_previous = i_glyph_index;
1624 *pi_pen_x += p_face->glyph->advance.x >> 6;
1627 p_line->i_width = line.xMax;
1628 p_line->i_height = __MAX( p_line->i_height,
1629 p_face->size->metrics.height >> 6 );
1630 p_line->pp_glyphs[ i ] = NULL;
1632 p_result->x = __MAX( p_result->x, line.xMax );
1633 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1634 line.yMax - line.yMin ) );
1638 /* Get rid of any text processed - if necessary repositioning
1639 * at the start of a new line of text
1643 *psz_unicode_start = '\0';
1645 else if( psz_unicode > psz_unicode_start )
1647 for( i=0; psz_unicode[ i ]; i++ )
1648 psz_unicode_start[ i ] = psz_unicode[ i ];
1649 psz_unicode_start[ i ] = '\0';
1655 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1656 uint32_t **psz_text_out, uint32_t *pi_runs,
1657 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1658 ft_style_t *p_style )
1660 uint32_t i_string_length = 0;
1662 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1663 *psz_text_out += i_string_length;
1665 if( ppp_styles && ppi_run_lengths )
1671 *ppp_styles = (ft_style_t **)
1672 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1674 else if( *pi_runs == 1 )
1676 *ppp_styles = (ft_style_t **)
1677 malloc( *pi_runs * sizeof( ft_style_t * ) );
1680 /* We have just malloc'ed this memory successfully -
1681 * *pi_runs HAS to be within the memory area of *ppp_styles */
1684 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1688 if( *ppi_run_lengths )
1690 *ppi_run_lengths = (uint32_t *)
1691 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1693 else if( *pi_runs == 1 )
1695 *ppi_run_lengths = (uint32_t *)
1696 malloc( *pi_runs * sizeof( uint32_t ) );
1699 /* same remarks here */
1700 if( *ppi_run_lengths )
1702 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1705 /* If we couldn't use the p_style argument due to memory allocation
1706 * problems above, release it here.
1708 if( p_style ) DeleteStyle( p_style );
1711 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1715 for( k=0; k < p_sys->i_font_attachments; k++ )
1717 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1719 FT_Face p_face = NULL;
1721 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1729 bool match = !strcasecmp( p_face->family_name,
1730 p_style->psz_fontname );
1732 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1733 match = match && p_style->b_bold;
1735 match = match && !p_style->b_bold;
1737 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1738 match = match && p_style->b_italic;
1740 match = match && !p_style->b_italic;
1748 FT_Done_Face( p_face );
1753 return VLC_EGENERIC;
1756 static int ProcessLines( filter_t *p_filter,
1761 uint32_t *pi_run_lengths,
1762 ft_style_t **pp_styles,
1763 line_desc_t **pp_lines,
1765 FT_Vector *p_result,
1769 uint32_t *pi_k_run_lengths,
1770 uint32_t *pi_k_durations )
1772 filter_sys_t *p_sys = p_filter->p_sys;
1773 ft_style_t **pp_char_styles;
1774 int *p_new_positions = NULL;
1775 int8_t *p_levels = NULL;
1776 uint8_t *pi_karaoke_bar = NULL;
1780 /* Assign each character in the text string its style explicitly, so that
1781 * after the characters have been shuffled around by Fribidi, we can re-apply
1782 * the styles, and to simplify the calculation of runs within a line.
1784 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1785 if( !pp_char_styles )
1790 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1791 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1792 * we just won't be able to display the progress bar; at least we'll
1798 for( j = 0; j < i_runs; j++ )
1799 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1800 pp_char_styles[ i++ ] = pp_styles[ j ];
1802 #if defined(HAVE_FRIBIDI)
1804 ft_style_t **pp_char_styles_new;
1805 int *p_old_positions;
1806 uint32_t *p_fribidi_string;
1807 int start_pos, pos = 0;
1809 pp_char_styles_new = (ft_style_t **)
1810 malloc( i_len * sizeof( ft_style_t * ));
1812 p_fribidi_string = (uint32_t *)
1813 malloc( (i_len + 1) * sizeof(uint32_t) );
1814 p_old_positions = (int *)
1815 malloc( (i_len + 1) * sizeof( int ) );
1816 p_new_positions = (int *)
1817 malloc( (i_len + 1) * sizeof( int ) );
1818 p_levels = (int8_t *)
1819 malloc( (i_len + 1) * sizeof( int8_t ) );
1821 if( ! pp_char_styles_new ||
1822 ! p_fribidi_string ||
1823 ! p_old_positions ||
1824 ! p_new_positions ||
1828 free( p_old_positions );
1829 free( p_new_positions );
1830 free( p_fribidi_string );
1831 free( pp_char_styles_new );
1832 free( pi_karaoke_bar );
1834 free( pp_char_styles );
1838 /* Do bidi conversion line-by-line */
1841 while(pos < i_len) {
1842 if (psz_text[pos] != '\n')
1844 p_fribidi_string[pos] = psz_text[pos];
1845 pp_char_styles_new[pos] = pp_char_styles[pos];
1846 p_new_positions[pos] = pos;
1851 while(pos < i_len) {
1852 if (psz_text[pos] == '\n')
1856 if (pos > start_pos)
1858 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1859 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1860 pos - start_pos, &base_dir,
1861 (FriBidiChar*)p_fribidi_string + start_pos,
1862 p_new_positions + start_pos,
1864 p_levels + start_pos );
1865 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1867 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1868 p_old_positions[ j - start_pos ] ];
1869 p_new_positions[ j ] += start_pos;
1873 free( p_old_positions );
1874 free( pp_char_styles );
1875 pp_char_styles = pp_char_styles_new;
1876 psz_text = p_fribidi_string;
1877 p_fribidi_string[ i_len ] = 0;
1880 /* Work out the karaoke */
1881 if( pi_karaoke_bar )
1883 int64_t i_last_duration = 0;
1884 int64_t i_duration = 0;
1885 int64_t i_start_pos = 0;
1886 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1888 for( k = 0; k< i_k_runs; k++ )
1890 double fraction = 0.0;
1892 i_duration += pi_k_durations[ k ];
1894 if( i_duration < i_elapsed )
1896 /* Completely finished this run-length -
1897 * let it render normally */
1901 else if( i_elapsed < i_last_duration )
1903 /* Haven't got up to this segment yet -
1904 * render it completely in karaoke BG mode */
1910 /* Partway through this run */
1912 fraction = (double)(i_elapsed - i_last_duration) /
1913 (double)pi_k_durations[ k ];
1915 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1917 double shade = pi_k_run_lengths[ k ] * fraction;
1919 if( p_new_positions )
1920 j = p_new_positions[ i_start_pos + i ];
1922 j = i_start_pos + i;
1924 if( i < (uint32_t)shade )
1925 pi_karaoke_bar[ j ] = 0xff;
1926 else if( (double)i > shade )
1927 pi_karaoke_bar[ j ] = 0x00;
1930 shade -= (int)shade;
1931 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1932 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1936 i_last_duration = i_duration;
1937 i_start_pos += pi_k_run_lengths[ k ];
1941 free( p_new_positions );
1943 FT_Vector tmp_result;
1945 line_desc_t *p_line = NULL;
1946 line_desc_t *p_prev = NULL;
1952 p_result->x = p_result->y = 0;
1953 tmp_result.x = tmp_result.y = 0;
1956 for( k = 0; k <= (uint32_t) i_len; k++ )
1958 if( ( k == (uint32_t) i_len ) ||
1960 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1962 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1964 /* End of the current style run */
1965 FT_Face p_face = NULL;
1968 /* Look for a match amongst our attachments first */
1969 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1973 char *psz_fontfile = NULL;
1975 psz_fontfile = FontConfig_Select( NULL,
1976 p_style->psz_fontname,
1980 if( psz_fontfile && ! *psz_fontfile )
1982 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1983 " so using default font", p_style->psz_fontname,
1984 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1985 (p_style->b_bold ? "(Bold)" :
1986 (p_style->b_italic ? "(Italic)" : ""))) );
1987 free( psz_fontfile );
1988 psz_fontfile = NULL;
1993 if( FT_New_Face( p_sys->p_library,
1994 psz_fontfile, i_idx, &p_face ) )
1996 free( psz_fontfile );
1997 free( pp_char_styles );
1998 #if defined(HAVE_FRIBIDI)
2001 free( pi_karaoke_bar );
2002 return VLC_EGENERIC;
2004 free( psz_fontfile );
2008 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2010 /* We've loaded a font face which is unhelpful for actually
2011 * rendering text - fallback to the default one.
2013 FT_Done_Face( p_face );
2017 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2018 ft_encoding_unicode ) ||
2019 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2020 p_style->i_font_size ) )
2022 if( p_face ) FT_Done_Face( p_face );
2023 free( pp_char_styles );
2024 #if defined(HAVE_FRIBIDI)
2027 free( pi_karaoke_bar );
2028 return VLC_EGENERIC;
2030 p_sys->i_use_kerning =
2031 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2034 uint32_t *psz_unicode = (uint32_t *)
2035 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2038 if( p_face ) FT_Done_Face( p_face );
2039 free( pp_char_styles );
2040 free( psz_unicode );
2041 #if defined(HAVE_FRIBIDI)
2044 free( pi_karaoke_bar );
2047 memcpy( psz_unicode, psz_text + i_prev,
2048 sizeof( uint32_t ) * ( k - i_prev ) );
2049 psz_unicode[ k - i_prev ] = 0;
2050 while( *psz_unicode )
2054 if( !(p_line = NewLine( i_len - i_prev)) )
2056 if( p_face ) FT_Done_Face( p_face );
2057 free( pp_char_styles );
2058 free( psz_unicode );
2059 #if defined(HAVE_FRIBIDI)
2062 free( pi_karaoke_bar );
2065 /* New Color mode only works in YUVA rendering mode --
2066 * (RGB mode has palette constraints on it). We therefore
2067 * need to populate the legacy colour fields also.
2069 p_line->b_new_color_mode = true;
2070 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2071 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2072 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2073 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2074 p_line->p_next = NULL;
2076 i_pen_y += tmp_result.y;
2080 if( p_prev ) p_prev->p_next = p_line;
2081 else *pp_lines = p_line;
2084 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2085 p_style->i_font_color, p_style->b_underline,
2086 p_style->i_karaoke_bg_color,
2087 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2088 &tmp_result ) != VLC_SUCCESS )
2090 if( p_face ) FT_Done_Face( p_face );
2091 free( pp_char_styles );
2092 free( psz_unicode );
2093 #if defined(HAVE_FRIBIDI)
2096 free( pi_karaoke_bar );
2097 return VLC_EGENERIC;
2102 p_result->x = __MAX( p_result->x, tmp_result.x );
2103 p_result->y += tmp_result.y;
2108 if( *psz_unicode == '\n')
2112 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2114 *c_ptr = *(c_ptr+1);
2119 free( psz_unicode );
2120 if( p_face ) FT_Done_Face( p_face );
2124 free( pp_char_styles );
2125 #if defined(HAVE_FRIBIDI)
2130 p_result->x = __MAX( p_result->x, tmp_result.x );
2131 p_result->y += tmp_result.y;
2134 if( pi_karaoke_bar )
2137 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2139 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2141 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2145 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2147 /* 100% BG colour will render faster if we
2148 * instead make it 100% FG colour, so leave
2149 * the ratio alone and copy the value across
2151 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2155 if( pi_karaoke_bar[ i ] & 0x80 )
2157 /* Swap Left and Right sides over for Right aligned
2158 * language text (eg. Arabic, Hebrew)
2160 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2162 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2163 p_line->p_bg_rgb[ k ] = i_tmp;
2165 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2168 /* Jump over the '\n' at the line-end */
2171 free( pi_karaoke_bar );
2177 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2178 subpicture_region_t *p_region_in )
2180 int rv = VLC_SUCCESS;
2181 stream_t *p_sub = NULL;
2182 xml_reader_t *p_xml_reader = NULL;
2184 if( !p_region_in || !p_region_in->psz_html )
2185 return VLC_EGENERIC;
2187 /* Reset the default fontsize in case screen metrics have changed */
2188 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2190 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2191 (uint8_t *) p_region_in->psz_html,
2192 strlen( p_region_in->psz_html ),
2196 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2197 if( p_filter->p_sys->p_xml )
2199 bool b_karaoke = false;
2201 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2204 /* Look for Root Node */
2205 if( xml_ReaderRead( p_xml_reader ) == 1 )
2207 char *psz_node = xml_ReaderName( p_xml_reader );
2209 if( !strcasecmp( "karaoke", psz_node ) )
2211 /* We're going to have to render the text a number
2212 * of times to show the progress marker on the text.
2214 var_SetBool( p_filter, "text-rerender", true );
2217 else if( !strcasecmp( "text", psz_node ) )
2223 /* Only text and karaoke tags are supported */
2224 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2225 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2226 p_xml_reader = NULL;
2238 uint32_t i_runs = 0;
2239 uint32_t i_k_runs = 0;
2240 uint32_t *pi_run_lengths = NULL;
2241 uint32_t *pi_k_run_lengths = NULL;
2242 uint32_t *pi_k_durations = NULL;
2243 ft_style_t **pp_styles = NULL;
2245 line_desc_t *p_lines = NULL;
2247 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2248 sizeof( uint32_t ) );
2253 rv = ProcessNodes( p_filter, p_xml_reader,
2254 p_region_in->p_style, psz_text, &i_len,
2255 &i_runs, &pi_run_lengths, &pp_styles,
2257 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2260 p_region_out->i_x = p_region_in->i_x;
2261 p_region_out->i_y = p_region_in->i_y;
2263 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2265 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2266 pi_run_lengths, pp_styles, &p_lines, &result,
2267 b_karaoke, i_k_runs, pi_k_run_lengths,
2271 for( k=0; k<i_runs; k++)
2272 DeleteStyle( pp_styles[k] );
2274 free( pi_run_lengths );
2277 /* Don't attempt to render text that couldn't be layed out
2280 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2282 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2284 Render( p_filter, p_region_out, p_lines,
2285 result.x, result.y );
2289 RenderYUVA( p_filter, p_region_out, p_lines,
2290 result.x, result.y );
2294 FreeLines( p_lines );
2296 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2299 stream_Delete( p_sub );
2305 static char* FontConfig_Select( FcConfig* priv, const char* family,
2306 bool b_bold, bool b_italic, int *i_idx )
2309 FcPattern *pat, *p_pat;
2313 pat = FcPatternCreate();
2314 if (!pat) return NULL;
2316 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2317 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2318 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2319 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2321 FcDefaultSubstitute( pat );
2323 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2325 FcPatternDestroy( pat );
2329 p_pat = FcFontMatch( priv, pat, &result );
2330 FcPatternDestroy( pat );
2331 if( !p_pat ) return NULL;
2333 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2334 || ( val_b != FcTrue ) )
2336 FcPatternDestroy( p_pat );
2339 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2344 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2346 FcPatternDestroy( p_pat );
2351 if( strcasecmp((const char*)val_s, family ) != 0 )
2352 msg_Warn( p_filter, "fontconfig: selected font family is not"
2353 "the requested one: '%s' != '%s'\n",
2354 (const char*)val_s, family );
2357 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2359 FcPatternDestroy( p_pat );
2363 FcPatternDestroy( p_pat );
2364 return strdup( (const char*)val_s );
2368 static void FreeLine( line_desc_t *p_line )
2371 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2373 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2375 free( p_line->pp_glyphs );
2376 free( p_line->p_glyph_pos );
2377 free( p_line->p_fg_rgb );
2378 free( p_line->p_bg_rgb );
2379 free( p_line->p_fg_bg_ratio );
2380 free( p_line->pi_underline_offset );
2381 free( p_line->pi_underline_thickness );
2385 static void FreeLines( line_desc_t *p_lines )
2387 line_desc_t *p_line, *p_next;
2389 if( !p_lines ) return;
2391 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2393 p_next = p_line->p_next;
2398 static line_desc_t *NewLine( int i_count )
2400 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2402 if( !p_line ) return NULL;
2403 p_line->i_height = 0;
2404 p_line->i_width = 0;
2405 p_line->p_next = NULL;
2407 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2408 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2409 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2410 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2411 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2412 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2413 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2414 if( ( p_line->pp_glyphs == NULL ) ||
2415 ( p_line->p_glyph_pos == NULL ) ||
2416 ( p_line->p_fg_rgb == NULL ) ||
2417 ( p_line->p_bg_rgb == NULL ) ||
2418 ( p_line->p_fg_bg_ratio == NULL ) ||
2419 ( p_line->pi_underline_offset == NULL ) ||
2420 ( p_line->pi_underline_thickness == NULL ) )
2422 free( p_line->pi_underline_thickness );
2423 free( p_line->pi_underline_offset );
2424 free( p_line->p_fg_rgb );
2425 free( p_line->p_bg_rgb );
2426 free( p_line->p_fg_bg_ratio );
2427 free( p_line->p_glyph_pos );
2428 free( p_line->pp_glyphs );
2432 p_line->pp_glyphs[0] = NULL;
2433 p_line->b_new_color_mode = false;
2438 static int GetFontSize( filter_t *p_filter )
2440 filter_sys_t *p_sys = p_filter->p_sys;
2444 if( p_sys->i_default_font_size )
2446 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2447 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2449 i_size = p_sys->i_default_font_size;
2453 var_Get( p_filter, "freetype-rel-fontsize", &val );
2456 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2457 p_filter->p_sys->i_display_height =
2458 p_filter->fmt_out.video.i_height;
2463 msg_Warn( p_filter, "invalid fontsize, using 12" );
2464 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2465 i_size = 12 * val.i_int / 1000;
2472 static int SetFontSize( filter_t *p_filter, int i_size )
2474 filter_sys_t *p_sys = p_filter->p_sys;
2478 i_size = GetFontSize( p_filter );
2480 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2483 p_sys->i_font_size = i_size;
2485 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2487 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2488 return VLC_EGENERIC;
2494 static void YUVFromRGB( uint32_t i_argb,
2495 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2497 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2498 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2499 int i_blue = ( i_argb & 0x000000ff );
2501 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2502 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2503 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2504 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2505 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2506 -585 * i_blue + 4096 + 1048576) >> 13, 240);