1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
42 #include <vlc_dialog.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
57 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
58 #define FC_DEFAULT_FONT "Arial Black"
59 #elif defined( SYS_BEOS )
60 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
61 #define FC_DEFAULT_FONT "Swiss"
62 #elif defined( WIN32 )
63 #define DEFAULT_FONT "" /* Default font found at run-time */
64 #define FC_DEFAULT_FONT "Arial"
66 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
67 #define FC_DEFAULT_FONT "Serif Bold"
70 #if defined(HAVE_FRIBIDI)
71 #include <fribidi/fribidi.h>
74 #ifdef HAVE_FONTCONFIG
75 #include <fontconfig/fontconfig.h>
77 #define DEFAULT_FONT FC_DEFAULT_FONT
82 /*****************************************************************************
84 *****************************************************************************/
85 static int Create ( vlc_object_t * );
86 static void Destroy( vlc_object_t * );
88 #define FONT_TEXT N_("Font")
90 #ifdef HAVE_FONTCONFIG
91 #define FONT_LONGTEXT N_("Font family for the font you want to use")
93 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
96 #define FONTSIZE_TEXT N_("Font size in pixels")
97 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
98 "that will be rendered on the video. " \
99 "If set to something different than 0 this option will override the " \
100 "relative font size." )
101 #define OPACITY_TEXT N_("Opacity")
102 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
103 "text that will be rendered on the video. 0 = transparent, " \
104 "255 = totally opaque. " )
105 #define COLOR_TEXT N_("Text default color")
106 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
107 "the video. This must be an hexadecimal (like HTML colors). The first two "\
108 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
109 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
110 #define FONTSIZER_TEXT N_("Relative font size")
111 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
112 "fonts that will be rendered on the video. If absolute font size is set, "\
113 "relative size will be overriden." )
115 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
116 static const char *const ppsz_sizes_text[] = {
117 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
118 #define YUVP_TEXT N_("Use YUVP renderer")
119 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
120 "This option is only needed if you want to encode into DVB subtitles" )
121 #define EFFECT_TEXT N_("Font Effect")
122 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
123 "text to improve its readability." )
125 #define EFFECT_BACKGROUND 1
126 #define EFFECT_OUTLINE 2
127 #define EFFECT_OUTLINE_FAT 3
129 static int const pi_effects[] = { 1, 2, 3 };
130 static const char *const ppsz_effects_text[] = {
131 N_("Background"),N_("Outline"), N_("Fat Outline") };
132 static const int pi_color_values[] = {
133 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
134 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
135 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
137 static const char *const ppsz_color_descriptions[] = {
138 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
139 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
140 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
143 set_shortname( N_("Text renderer"))
144 set_description( N_("Freetype2 font renderer") )
145 set_category( CAT_VIDEO )
146 set_subcategory( SUBCAT_VIDEO_SUBPIC )
148 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
151 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
152 FONTSIZE_LONGTEXT, true )
154 /* opacity valid on 0..255, with default 255 = fully opaque */
155 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
156 OPACITY_TEXT, OPACITY_LONGTEXT, true )
158 /* hook to the color values list, with default 0x00ffffff = white */
159 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
160 COLOR_LONGTEXT, false )
161 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
163 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
164 FONTSIZER_LONGTEXT, false )
165 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
166 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
167 EFFECT_LONGTEXT, false )
168 change_integer_list( pi_effects, ppsz_effects_text, NULL )
170 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
171 YUVP_LONGTEXT, true )
172 set_capability( "text renderer", 100 )
173 add_shortcut( "text" )
174 set_callbacks( Create, Destroy )
179 /*****************************************************************************
181 *****************************************************************************/
183 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
184 static int RenderText( filter_t *, subpicture_region_t *,
185 subpicture_region_t * );
186 #ifdef HAVE_FONTCONFIG
187 static int RenderHtml( filter_t *, subpicture_region_t *,
188 subpicture_region_t * );
189 static char *FontConfig_Select( FcConfig *, const char *,
194 static int LoadFontsFromAttachments( filter_t *p_filter );
196 static int GetFontSize( filter_t *p_filter );
197 static int SetFontSize( filter_t *, int );
198 static void YUVFromRGB( uint32_t i_argb,
199 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
201 typedef struct line_desc_t line_desc_t;
204 /** NULL-terminated list of glyphs making the string */
205 FT_BitmapGlyph *pp_glyphs;
206 /** list of relative positions for the glyphs */
207 FT_Vector *p_glyph_pos;
208 /** list of RGB information for styled text
209 * -- if the rendering mode supports it (RenderYUVA) and
210 * b_new_color_mode is set, then it becomes possible to
211 * have multicoloured text within the subtitles. */
214 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
215 bool b_new_color_mode;
216 /** underline information -- only supplied if text should be underlined */
217 uint16_t *pi_underline_offset;
218 uint16_t *pi_underline_thickness;
222 int i_red, i_green, i_blue;
227 static line_desc_t *NewLine( int );
232 uint32_t i_font_color; /* ARGB */
233 uint32_t i_karaoke_bg_color; /* ARGB */
240 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
241 static void FreeLines( line_desc_t * );
242 static void FreeLine( line_desc_t * );
244 /*****************************************************************************
245 * filter_sys_t: freetype local data
246 *****************************************************************************
247 * This structure is part of the video output thread descriptor.
248 * It describes the freetype specific properties of an output thread.
249 *****************************************************************************/
252 FT_Library p_library; /* handle to library */
253 FT_Face p_face; /* handle to face object */
255 uint8_t i_font_opacity;
260 int i_default_font_size;
261 int i_display_height;
262 #ifdef HAVE_FONTCONFIG
263 char* psz_fontfamily;
267 input_attachment_t **pp_font_attachments;
268 int i_font_attachments;
272 #define UCHAR uint32_t
273 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
274 #define TR_FONT_STYLE_PTR ft_style_t *
276 #include "text_renderer.h"
278 /*****************************************************************************
279 * Create: allocates osd-text video thread output method
280 *****************************************************************************
281 * This function allocates and initializes a Clone vout method.
282 *****************************************************************************/
283 static int Create( vlc_object_t *p_this )
285 filter_t *p_filter = (filter_t *)p_this;
287 char *psz_fontfile=NULL;
288 char *psz_fontfamily=NULL;
289 int i_error,fontindex;
291 #ifdef HAVE_FONTCONFIG
292 FcPattern *fontpattern = NULL, *fontmatch = NULL;
293 /* Initialise result to Match, as fontconfig doesnt
294 * really set this other than some error-cases */
295 FcResult fontresult = FcResultMatch;
299 /* Allocate structure */
300 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
303 #ifdef HAVE_FONTCONFIG
304 p_sys->psz_fontfamily = NULL;
308 p_sys->p_library = 0;
309 p_sys->i_font_size = 0;
310 p_sys->i_display_height = 0;
312 var_Create( p_filter, "freetype-rel-fontsize",
313 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
315 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
316 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
317 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
318 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
319 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
320 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
321 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
324 if( !psz_fontfamily || !*psz_fontfamily )
326 free( psz_fontfamily );
327 #ifdef HAVE_FONTCONFIG
328 psz_fontfamily=strdup( DEFAULT_FONT );
330 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
331 if( !psz_fontfamily )
334 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
335 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
337 strcpy( psz_fontfamily, DEFAULT_FONT );
339 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
343 #ifdef HAVE_FONTCONFIG
344 /* Lets find some fontfile from freetype-font variable family */
346 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
350 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
351 _("Building font cache"),
352 _("Please wait while your font cache is rebuilt.\n"
353 "This should take less than few minutes."), NULL );
355 path = (char *)malloc( PATH_MAX + 1 );
356 /* Fontconfig doesnt seem to know where windows fonts are with
357 * current contribs. So just tell default windows font directory
358 * is the place to search fonts
360 GetWindowsDirectory( path, PATH_MAX + 1 );
361 strcat( path, "\\fonts" );
363 dialog_ProgressSet( p_dialog, NULL, 0.4 );
365 FcConfigAppFontAddDir( NULL , path );
370 dialog_ProgressSet( p_dialog, NULL, 0.5 );
374 msg_Dbg( p_filter, "Building font database.");
376 FcConfigBuildFonts( NULL );
379 msg_Dbg( p_filter, "Finished building font database." );
380 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
382 fontpattern = FcPatternCreate();
386 msg_Err( p_filter, "Creating fontpattern failed");
392 dialog_ProgressSet( p_dialog, NULL, 0.7 );
394 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
395 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
396 free( psz_fontsize );
398 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
400 msg_Err( p_filter, "FontSubstitute failed");
403 FcDefaultSubstitute( fontpattern );
407 dialog_ProgressSet( p_dialog, NULL, 0.8 );
409 /* testing fontresult here doesn't do any good really, but maybe it will
410 * in future as fontconfig code doesn't set it in all cases and just
411 * returns NULL or doesn't set to to Match on all Match cases.*/
412 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
413 if( !fontmatch || fontresult == FcResultNoMatch )
415 msg_Err( p_filter, "Fontmatching failed");
419 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
420 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
423 msg_Err( p_filter, "Failed to get fontfile");
427 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
428 p_sys->psz_fontfamily = strdup( psz_fontfamily );
432 dialog_ProgressSet( p_dialog, NULL, 1.0 );
433 dialog_ProgressDestroy( p_dialog );
439 #ifdef HAVE_FONTCONFIG
440 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
441 psz_fontfile = psz_fontfamily;
446 i_error = FT_Init_FreeType( &p_sys->p_library );
449 msg_Err( p_filter, "couldn't initialize freetype" );
453 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
454 fontindex, &p_sys->p_face );
456 if( i_error == FT_Err_Unknown_File_Format )
458 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
463 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
467 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
470 msg_Err( p_filter, "font has no unicode translation table" );
474 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
476 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
479 p_sys->pp_font_attachments = NULL;
480 p_sys->i_font_attachments = 0;
482 p_filter->pf_render_text = RenderText;
483 #ifdef HAVE_FONTCONFIG
484 p_filter->pf_render_html = RenderHtml;
485 FcPatternDestroy( fontmatch );
486 FcPatternDestroy( fontpattern );
488 p_filter->pf_render_html = NULL;
491 free( psz_fontfamily );
492 LoadFontsFromAttachments( p_filter );
497 #ifdef HAVE_FONTCONFIG
498 if( fontmatch ) FcPatternDestroy( fontmatch );
499 if( fontpattern ) FcPatternDestroy( fontpattern );
501 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
502 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
503 free( psz_fontfamily );
508 /*****************************************************************************
509 * Destroy: destroy Clone video thread output method
510 *****************************************************************************
511 * Clean up all data and library connections
512 *****************************************************************************/
513 static void Destroy( vlc_object_t *p_this )
515 filter_t *p_filter = (filter_t *)p_this;
516 filter_sys_t *p_sys = p_filter->p_sys;
518 if( p_sys->pp_font_attachments )
522 for( k = 0; k < p_sys->i_font_attachments; k++ )
523 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
525 free( p_sys->pp_font_attachments );
528 #ifdef HAVE_FONTCONFIG
529 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
530 free( p_sys->psz_fontfamily );
533 /* FcFini asserts calling the subfunction FcCacheFini()
534 * even if no other library functions have been made since FcInit(),
535 * so don't call it. */
537 FT_Done_Face( p_sys->p_face );
538 FT_Done_FreeType( p_sys->p_library );
542 /*****************************************************************************
543 * Make any TTF/OTF fonts present in the attachments of the media file
544 * and store them for later use by the FreeType Engine
545 *****************************************************************************/
546 static int LoadFontsFromAttachments( filter_t *p_filter )
548 filter_sys_t *p_sys = p_filter->p_sys;
549 input_thread_t *p_input;
550 input_attachment_t **pp_attachments;
551 int i_attachments_cnt;
553 int rv = VLC_SUCCESS;
555 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
559 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
561 vlc_object_release(p_input);
565 p_sys->i_font_attachments = 0;
566 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
567 if(! p_sys->pp_font_attachments )
570 for( k = 0; k < i_attachments_cnt; k++ )
572 input_attachment_t *p_attach = pp_attachments[k];
574 if( p_sys->pp_font_attachments )
576 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
577 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
578 ( p_attach->i_data > 0 ) &&
579 ( p_attach->p_data != NULL ) )
581 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
585 vlc_input_attachment_Delete( p_attach );
590 vlc_input_attachment_Delete( p_attach );
593 free( pp_attachments );
595 vlc_object_release(p_input);
600 /*****************************************************************************
601 * Render: place string in picture
602 *****************************************************************************
603 * This function merges the previously rendered freetype glyphs into a picture
604 *****************************************************************************/
605 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
606 line_desc_t *p_line, int i_width, int i_height )
608 VLC_UNUSED(p_filter);
609 static const uint8_t pi_gamma[16] =
610 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
611 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
615 int i, x, y, i_pitch;
616 uint8_t i_y; /* YUV values, derived from incoming RGB */
619 /* Create a new subpicture region */
620 memset( &fmt, 0, sizeof(video_format_t) );
621 fmt.i_chroma = VLC_CODEC_YUVP;
623 fmt.i_width = fmt.i_visible_width = i_width + 4;
624 fmt.i_height = fmt.i_visible_height = i_height + 4;
625 if( p_region->fmt.i_visible_width > 0 )
626 fmt.i_visible_width = p_region->fmt.i_visible_width;
627 if( p_region->fmt.i_visible_height > 0 )
628 fmt.i_visible_height = p_region->fmt.i_visible_height;
629 fmt.i_x_offset = fmt.i_y_offset = 0;
631 assert( !p_region->p_picture );
632 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
633 if( !p_region->p_picture )
637 /* Calculate text color components */
638 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
639 25 * p_line->i_blue + 128) >> 8) + 16;
640 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
641 112 * p_line->i_blue + 128) >> 8) + 128;
642 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
643 18 * p_line->i_blue + 128) >> 8) + 128;
646 fmt.p_palette->i_entries = 16;
647 for( i = 0; i < 8; i++ )
649 fmt.p_palette->palette[i][0] = 0;
650 fmt.p_palette->palette[i][1] = 0x80;
651 fmt.p_palette->palette[i][2] = 0x80;
652 fmt.p_palette->palette[i][3] = pi_gamma[i];
653 fmt.p_palette->palette[i][3] =
654 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
656 for( i = 8; i < fmt.p_palette->i_entries; i++ )
658 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
659 fmt.p_palette->palette[i][1] = i_u;
660 fmt.p_palette->palette[i][2] = i_v;
661 fmt.p_palette->palette[i][3] = pi_gamma[i];
662 fmt.p_palette->palette[i][3] =
663 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
666 p_dst = p_region->p_picture->Y_PIXELS;
667 i_pitch = p_region->p_picture->Y_PITCH;
669 /* Initialize the region pixels */
670 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
672 for( ; p_line != NULL; p_line = p_line->p_next )
674 int i_glyph_tmax = 0;
675 int i_bitmap_offset, i_offset, i_align_offset = 0;
676 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
678 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
679 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
682 if( p_line->i_width < i_width )
684 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
686 i_align_offset = i_width - p_line->i_width;
688 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
690 i_align_offset = ( i_width - p_line->i_width ) / 2;
694 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
696 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
698 i_offset = ( p_line->p_glyph_pos[ i ].y +
699 i_glyph_tmax - p_glyph->top + 2 ) *
700 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
703 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
705 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
707 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
709 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
716 /* Outlining (find something better than nearest neighbour filtering ?) */
719 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
720 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
721 uint8_t left, current;
723 for( y = 1; y < (int)fmt.i_height - 1; y++ )
725 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
726 p_dst += p_region->p_picture->Y_PITCH;
729 for( x = 1; x < (int)fmt.i_width - 1; x++ )
732 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
733 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;
737 memset( p_top, 0, fmt.i_width );
743 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
744 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
745 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
746 int i_glyph_tmax, int i_align_offset,
747 uint8_t i_y, uint8_t i_u, uint8_t i_v,
748 subpicture_region_t *p_region)
752 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
754 p_dst_y = p_region->p_picture->Y_PIXELS;
755 p_dst_u = p_region->p_picture->U_PIXELS;
756 p_dst_v = p_region->p_picture->V_PIXELS;
757 p_dst_a = p_region->p_picture->A_PIXELS;
758 i_pitch = p_region->p_picture->A_PITCH;
760 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
761 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
763 for( y = 0; y < i_line_thickness; y++ )
765 int i_extra = p_this_glyph->bitmap.width;
769 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
770 (p_this_glyph_pos->x + p_this_glyph->left);
772 for( x = 0; x < i_extra; x++ )
776 /* break the underline around the tails of any glyphs which cross it */
777 for( z = x - i_line_thickness;
778 z < x + i_line_thickness && b_ok;
781 if( p_next_glyph && ( z >= i_extra ) )
783 int i_row = i_line_offset + p_next_glyph->top + y;
785 if( ( p_next_glyph->bitmap.rows > i_row ) &&
786 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
791 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
793 int i_row = i_line_offset + p_this_glyph->top + y;
795 if( ( p_this_glyph->bitmap.rows > i_row ) &&
796 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
805 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
806 p_dst_u[i_offset+x] = i_u;
807 p_dst_v[i_offset+x] = i_v;
808 p_dst_a[i_offset+x] = 255;
815 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
817 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
818 int i_pitch = p_region->p_picture->A_PITCH;
821 for( ; p_line != NULL; p_line = p_line->p_next )
823 int i_glyph_tmax=0, i = 0;
824 int i_bitmap_offset, i_offset, i_align_offset = 0;
825 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
827 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
828 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
831 if( p_line->i_width < i_width )
833 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
835 i_align_offset = i_width - p_line->i_width;
837 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
839 i_align_offset = ( i_width - p_line->i_width ) / 2;
843 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
845 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
847 i_offset = ( p_line->p_glyph_pos[ i ].y +
848 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
849 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
850 i_align_offset +xoffset;
852 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
854 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
856 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
857 if( p_dst[i_offset+x] <
858 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
860 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
869 /*****************************************************************************
870 * Render: place string in picture
871 *****************************************************************************
872 * This function merges the previously rendered freetype glyphs into a picture
873 *****************************************************************************/
874 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
875 line_desc_t *p_line, int i_width, int i_height )
877 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
879 int i, x, y, i_pitch, i_alpha;
880 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
882 if( i_width == 0 || i_height == 0 )
885 /* Create a new subpicture region */
886 memset( &fmt, 0, sizeof(video_format_t) );
887 fmt.i_chroma = VLC_CODEC_YUVA;
889 fmt.i_width = fmt.i_visible_width = i_width + 6;
890 fmt.i_height = fmt.i_visible_height = i_height + 6;
891 if( p_region->fmt.i_visible_width > 0 )
892 fmt.i_visible_width = p_region->fmt.i_visible_width;
893 if( p_region->fmt.i_visible_height > 0 )
894 fmt.i_visible_height = p_region->fmt.i_visible_height;
895 fmt.i_x_offset = fmt.i_y_offset = 0;
897 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
898 if( !p_region->p_picture )
902 /* Calculate text color components */
903 YUVFromRGB( (p_line->i_red << 16) |
904 (p_line->i_green << 8) |
907 i_alpha = p_line->i_alpha;
909 p_dst_y = p_region->p_picture->Y_PIXELS;
910 p_dst_u = p_region->p_picture->U_PIXELS;
911 p_dst_v = p_region->p_picture->V_PIXELS;
912 p_dst_a = p_region->p_picture->A_PIXELS;
913 i_pitch = p_region->p_picture->A_PITCH;
915 /* Initialize the region pixels */
916 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
918 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
919 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
920 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
921 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
925 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
926 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
927 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
928 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
930 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
931 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
933 DrawBlack( p_line, i_width, p_region, 0, 0);
934 DrawBlack( p_line, i_width, p_region, -1, 0);
935 DrawBlack( p_line, i_width, p_region, 0, -1);
936 DrawBlack( p_line, i_width, p_region, 1, 0);
937 DrawBlack( p_line, i_width, p_region, 0, 1);
940 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
942 DrawBlack( p_line, i_width, p_region, -1, -1);
943 DrawBlack( p_line, i_width, p_region, -1, 1);
944 DrawBlack( p_line, i_width, p_region, 1, -1);
945 DrawBlack( p_line, i_width, p_region, 1, 1);
947 DrawBlack( p_line, i_width, p_region, -2, 0);
948 DrawBlack( p_line, i_width, p_region, 0, -2);
949 DrawBlack( p_line, i_width, p_region, 2, 0);
950 DrawBlack( p_line, i_width, p_region, 0, 2);
952 DrawBlack( p_line, i_width, p_region, -2, -2);
953 DrawBlack( p_line, i_width, p_region, -2, 2);
954 DrawBlack( p_line, i_width, p_region, 2, -2);
955 DrawBlack( p_line, i_width, p_region, 2, 2);
957 DrawBlack( p_line, i_width, p_region, -3, 0);
958 DrawBlack( p_line, i_width, p_region, 0, -3);
959 DrawBlack( p_line, i_width, p_region, 3, 0);
960 DrawBlack( p_line, i_width, p_region, 0, 3);
963 for( ; p_line != NULL; p_line = p_line->p_next )
965 int i_glyph_tmax = 0;
966 int i_bitmap_offset, i_offset, i_align_offset = 0;
967 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
969 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
970 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
973 if( p_line->i_width < i_width )
975 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
977 i_align_offset = i_width - p_line->i_width;
979 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
981 i_align_offset = ( i_width - p_line->i_width ) / 2;
985 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
987 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
989 i_offset = ( p_line->p_glyph_pos[ i ].y +
990 i_glyph_tmax - p_glyph->top + 3 ) *
991 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
994 if( p_line->b_new_color_mode )
996 /* Every glyph can (and in fact must) have its own color */
997 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1000 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1002 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1004 uint8_t i_y_local = i_y;
1005 uint8_t i_u_local = i_u;
1006 uint8_t i_v_local = i_v;
1008 if( p_line->p_fg_bg_ratio != 0x00 )
1010 int i_split = p_glyph->bitmap.width *
1011 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1015 YUVFromRGB( p_line->p_bg_rgb[ i ],
1016 &i_y_local, &i_u_local, &i_v_local );
1020 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1022 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1023 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1025 p_dst_u[i_offset+x] = i_u;
1026 p_dst_v[i_offset+x] = i_v;
1028 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1029 p_dst_a[i_offset+x] = 0xff;
1032 i_offset += i_pitch;
1035 if( p_line->pi_underline_thickness[ i ] )
1037 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1038 p_line->pi_underline_offset[ i ],
1039 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1040 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1041 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1042 i_glyph_tmax, i_align_offset,
1049 /* Apply the alpha setting */
1050 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1051 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1057 * This function renders a text subpicture region into another one.
1058 * It also calculates the size needed for this string, and renders the
1059 * needed glyphs into memory. It is used as pf_add_string callback in
1060 * the vout method by this module
1062 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1063 subpicture_region_t *p_region_in )
1065 filter_sys_t *p_sys = p_filter->p_sys;
1066 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1067 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1068 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1069 int i_string_length;
1071 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1072 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1082 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1083 psz_string = p_region_in->psz_text;
1084 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1086 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1087 i_scale = val.i_int;
1089 if( p_region_in->p_style )
1091 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1092 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1093 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1097 i_font_color = p_sys->i_font_color;
1098 i_font_alpha = 255 - p_sys->i_font_opacity;
1099 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1102 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1103 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1104 SetFontSize( p_filter, i_font_size );
1106 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1107 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1108 i_blue = i_font_color & 0x000000FF;
1110 result.x = result.y = 0;
1111 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1113 psz_unicode = psz_unicode_orig =
1114 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1115 if( psz_unicode == NULL )
1117 #if defined(WORDS_BIGENDIAN)
1118 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1120 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1122 if( iconv_handle == (vlc_iconv_t)-1 )
1124 msg_Warn( p_filter, "unable to do conversion" );
1130 const char *p_in_buffer = psz_string;
1131 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1132 i_in_bytes = strlen( psz_string );
1133 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1134 i_out_bytes_left = i_out_bytes;
1135 p_out_buffer = (char *)psz_unicode;
1136 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1138 &p_out_buffer, &i_out_bytes_left );
1140 vlc_iconv_close( iconv_handle );
1144 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1145 "bytes left %u", (unsigned)i_in_bytes );
1148 *(uint32_t*)p_out_buffer = 0;
1149 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1152 #if defined(HAVE_FRIBIDI)
1154 uint32_t *p_fribidi_string;
1155 int32_t start_pos, pos = 0;
1157 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1158 if( !p_fribidi_string )
1161 /* Do bidi conversion line-by-line */
1162 while( pos < i_string_length )
1164 while( pos < i_string_length )
1166 i_char = psz_unicode[pos];
1167 if (i_char != '\r' && i_char != '\n')
1169 p_fribidi_string[pos] = i_char;
1173 while( pos < i_string_length )
1175 i_char = psz_unicode[pos];
1176 if (i_char == '\r' || i_char == '\n')
1180 if (pos > start_pos)
1182 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1183 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1186 (FriBidiChar*)p_fribidi_string + start_pos,
1191 free( psz_unicode_orig );
1192 psz_unicode = psz_unicode_orig = p_fribidi_string;
1193 p_fribidi_string[ i_string_length ] = 0;
1197 /* Calculate relative glyph positions and a bounding box for the
1199 if( !(p_line = NewLine( strlen( psz_string ))) )
1202 i_pen_x = i_pen_y = 0;
1204 psz_line_start = psz_unicode;
1206 #define face p_sys->p_face
1207 #define glyph face->glyph
1209 while( *psz_unicode )
1211 i_char = *psz_unicode++;
1212 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1217 if( i_char == '\n' )
1219 psz_line_start = psz_unicode;
1220 if( !(p_next = NewLine( strlen( psz_string ))) )
1222 p_line->p_next = p_next;
1223 p_line->i_width = line.xMax;
1224 p_line->i_height = face->size->metrics.height >> 6;
1225 p_line->pp_glyphs[ i ] = NULL;
1226 p_line->i_alpha = i_font_alpha;
1227 p_line->i_red = i_red;
1228 p_line->i_green = i_green;
1229 p_line->i_blue = i_blue;
1232 result.x = __MAX( result.x, line.xMax );
1233 result.y += face->size->metrics.height >> 6;
1236 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1237 i_pen_y += face->size->metrics.height >> 6;
1239 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1244 i_glyph_index = FT_Get_Char_Index( face, i_char );
1245 if( p_sys->i_use_kerning && i_glyph_index
1249 FT_Get_Kerning( face, i_previous, i_glyph_index,
1250 ft_kerning_default, &delta );
1251 i_pen_x += delta.x >> 6;
1254 p_line->p_glyph_pos[ i ].x = i_pen_x;
1255 p_line->p_glyph_pos[ i ].y = i_pen_y;
1256 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1259 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1263 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1266 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1270 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1271 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1274 FT_Done_Glyph( tmp_glyph );
1277 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1280 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1281 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1282 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1284 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1285 p_line->pp_glyphs[ i ] = NULL;
1287 p_line = NewLine( strlen( psz_string ));
1288 if( p_prev ) p_prev->p_next = p_line;
1289 else p_lines = p_line;
1291 uint32_t *psz_unicode_saved = psz_unicode;
1292 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1296 if( psz_unicode == psz_line_start )
1297 { /* try harder to break that line */
1298 psz_unicode = psz_unicode_saved;
1299 while( psz_unicode > psz_line_start &&
1300 *psz_unicode != '_' && *psz_unicode != '/' &&
1301 *psz_unicode != '\\' && *psz_unicode != '.' )
1306 if( psz_unicode == psz_line_start )
1308 msg_Warn( p_filter, "unbreakable string" );
1313 *psz_unicode = '\n';
1315 psz_unicode = psz_line_start;
1318 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1321 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1322 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1324 i_previous = i_glyph_index;
1325 i_pen_x += glyph->advance.x >> 6;
1329 p_line->i_width = line.xMax;
1330 p_line->i_height = face->size->metrics.height >> 6;
1331 p_line->pp_glyphs[ i ] = NULL;
1332 p_line->i_alpha = i_font_alpha;
1333 p_line->i_red = i_red;
1334 p_line->i_green = i_green;
1335 p_line->i_blue = i_blue;
1336 result.x = __MAX( result.x, line.xMax );
1337 result.y += line.yMax - line.yMin;
1342 p_region_out->i_x = p_region_in->i_x;
1343 p_region_out->i_y = p_region_in->i_y;
1345 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1346 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1348 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1350 free( psz_unicode_orig );
1351 FreeLines( p_lines );
1355 free( psz_unicode_orig );
1356 FreeLines( p_lines );
1357 return VLC_EGENERIC;
1360 #ifdef HAVE_FONTCONFIG
1361 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1362 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1363 bool b_italic, bool b_uline )
1365 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1369 p_style->i_font_size = i_font_size;
1370 p_style->i_font_color = i_font_color;
1371 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1372 p_style->b_italic = b_italic;
1373 p_style->b_bold = b_bold;
1374 p_style->b_underline = b_uline;
1376 p_style->psz_fontname = strdup( psz_fontname );
1381 static void DeleteStyle( ft_style_t *p_style )
1385 free( p_style->psz_fontname );
1390 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1397 if(( s1->i_font_size == s2->i_font_size ) &&
1398 ( s1->i_font_color == s2->i_font_color ) &&
1399 ( s1->b_italic == s2->b_italic ) &&
1400 ( s1->b_bold == s2->b_bold ) &&
1401 ( s1->b_underline == s2->b_underline ) &&
1402 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1409 static void IconvText( filter_t *p_filter, const char *psz_string,
1410 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1412 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1414 /* If memory hasn't been allocated for our output string, allocate it here
1415 * - the calling function must now be responsible for freeing it.
1417 if( !*ppsz_unicode )
1418 *ppsz_unicode = (uint32_t *)
1419 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1421 /* We don't need to handle a NULL pointer in *ppsz_unicode
1422 * if we are instead testing for a non NULL value like we are here */
1426 #if defined(WORDS_BIGENDIAN)
1427 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1429 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1431 if( iconv_handle != (vlc_iconv_t)-1 )
1433 char *p_in_buffer, *p_out_buffer;
1434 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1435 i_in_bytes = strlen( psz_string );
1436 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1437 i_out_bytes_left = i_out_bytes;
1438 p_in_buffer = (char *) psz_string;
1439 p_out_buffer = (char *) *ppsz_unicode;
1440 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1441 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1443 vlc_iconv_close( iconv_handle );
1447 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1448 "bytes left %u", (unsigned)i_in_bytes );
1452 *(uint32_t*)p_out_buffer = 0;
1454 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1459 msg_Warn( p_filter, "unable to do conversion" );
1464 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1465 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1468 ft_style_t *p_style = NULL;
1470 char *psz_fontname = NULL;
1471 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1472 uint32_t i_karaoke_bg_color = i_font_color;
1473 int i_font_size = p_sys->i_font_size;
1475 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1476 &i_font_color, &i_karaoke_bg_color ))
1478 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1479 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1484 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1485 bool b_uline, int i_karaoke_bgcolor,
1486 line_desc_t *p_line, uint32_t *psz_unicode,
1487 int *pi_pen_x, int i_pen_y, int *pi_start,
1488 FT_Vector *p_result )
1493 bool b_first_on_line = true;
1496 int i_pen_x_start = *pi_pen_x;
1498 uint32_t *psz_unicode_start = psz_unicode;
1500 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1502 /* Account for part of line already in position */
1503 for( i=0; i<*pi_start; i++ )
1507 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1508 ft_glyph_bbox_pixels, &glyph_size );
1510 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1511 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1512 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1513 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1519 b_first_on_line = false;
1521 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1527 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1528 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1532 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1533 ft_kerning_default, &delta );
1534 *pi_pen_x += delta.x >> 6;
1536 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1537 p_line->p_glyph_pos[ i ].y = i_pen_y;
1539 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1543 "unable to render text FT_Load_Glyph returned %d", i_error );
1544 p_line->pp_glyphs[ i ] = NULL;
1545 return VLC_EGENERIC;
1547 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1551 "unable to render text FT_Get_Glyph returned %d", i_error );
1552 p_line->pp_glyphs[ i ] = NULL;
1553 return VLC_EGENERIC;
1555 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1556 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1559 FT_Done_Glyph( tmp_glyph );
1564 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1565 p_face->size->metrics.y_scale));
1566 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1567 p_face->size->metrics.y_scale));
1569 p_line->pi_underline_offset[ i ] =
1570 ( aOffset < 0 ) ? -aOffset : aOffset;
1571 p_line->pi_underline_thickness[ i ] =
1572 ( aSize < 0 ) ? -aSize : aSize;
1574 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1575 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1576 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1577 p_line->p_fg_bg_ratio[ i ] = 0x00;
1579 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1580 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1581 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1583 for( ; i >= *pi_start; i-- )
1584 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1587 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1591 if( psz_unicode == psz_unicode_start )
1593 if( b_first_on_line )
1595 msg_Warn( p_filter, "unbreakable string" );
1596 p_line->pp_glyphs[ i ] = NULL;
1597 return VLC_EGENERIC;
1599 *pi_pen_x = i_pen_x_start;
1601 p_line->i_width = line.xMax;
1602 p_line->i_height = __MAX( p_line->i_height,
1603 p_face->size->metrics.height >> 6 );
1604 p_line->pp_glyphs[ i ] = NULL;
1606 p_result->x = __MAX( p_result->x, line.xMax );
1607 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1608 i_yMax - i_yMin ) );
1613 *psz_unicode = '\n';
1615 psz_unicode = psz_unicode_start;
1616 *pi_pen_x = i_pen_x_start;
1624 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1625 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1627 i_previous = i_glyph_index;
1628 *pi_pen_x += p_face->glyph->advance.x >> 6;
1631 p_line->i_width = line.xMax;
1632 p_line->i_height = __MAX( p_line->i_height,
1633 p_face->size->metrics.height >> 6 );
1634 p_line->pp_glyphs[ i ] = NULL;
1636 p_result->x = __MAX( p_result->x, line.xMax );
1637 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1638 line.yMax - line.yMin ) );
1642 /* Get rid of any text processed - if necessary repositioning
1643 * at the start of a new line of text
1647 *psz_unicode_start = '\0';
1649 else if( psz_unicode > psz_unicode_start )
1651 for( i=0; psz_unicode[ i ]; i++ )
1652 psz_unicode_start[ i ] = psz_unicode[ i ];
1653 psz_unicode_start[ i ] = '\0';
1659 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1660 uint32_t **psz_text_out, uint32_t *pi_runs,
1661 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1662 ft_style_t *p_style )
1664 uint32_t i_string_length = 0;
1666 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1667 *psz_text_out += i_string_length;
1669 if( ppp_styles && ppi_run_lengths )
1675 *ppp_styles = (ft_style_t **)
1676 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1678 else if( *pi_runs == 1 )
1680 *ppp_styles = (ft_style_t **)
1681 malloc( *pi_runs * sizeof( ft_style_t * ) );
1684 /* We have just malloc'ed this memory successfully -
1685 * *pi_runs HAS to be within the memory area of *ppp_styles */
1688 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1692 if( *ppi_run_lengths )
1694 *ppi_run_lengths = (uint32_t *)
1695 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1697 else if( *pi_runs == 1 )
1699 *ppi_run_lengths = (uint32_t *)
1700 malloc( *pi_runs * sizeof( uint32_t ) );
1703 /* same remarks here */
1704 if( *ppi_run_lengths )
1706 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1709 /* If we couldn't use the p_style argument due to memory allocation
1710 * problems above, release it here.
1712 if( p_style ) DeleteStyle( p_style );
1715 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1719 for( k=0; k < p_sys->i_font_attachments; k++ )
1721 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1723 FT_Face p_face = NULL;
1725 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1733 bool match = !strcasecmp( p_face->family_name,
1734 p_style->psz_fontname );
1736 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1737 match = match && p_style->b_bold;
1739 match = match && !p_style->b_bold;
1741 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1742 match = match && p_style->b_italic;
1744 match = match && !p_style->b_italic;
1752 FT_Done_Face( p_face );
1757 return VLC_EGENERIC;
1760 static int ProcessLines( filter_t *p_filter,
1765 uint32_t *pi_run_lengths,
1766 ft_style_t **pp_styles,
1767 line_desc_t **pp_lines,
1769 FT_Vector *p_result,
1773 uint32_t *pi_k_run_lengths,
1774 uint32_t *pi_k_durations )
1776 filter_sys_t *p_sys = p_filter->p_sys;
1777 ft_style_t **pp_char_styles;
1778 int *p_new_positions = NULL;
1779 int8_t *p_levels = NULL;
1780 uint8_t *pi_karaoke_bar = NULL;
1784 /* Assign each character in the text string its style explicitly, so that
1785 * after the characters have been shuffled around by Fribidi, we can re-apply
1786 * the styles, and to simplify the calculation of runs within a line.
1788 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1789 if( !pp_char_styles )
1794 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1795 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1796 * we just won't be able to display the progress bar; at least we'll
1802 for( j = 0; j < i_runs; j++ )
1803 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1804 pp_char_styles[ i++ ] = pp_styles[ j ];
1806 #if defined(HAVE_FRIBIDI)
1808 ft_style_t **pp_char_styles_new;
1809 int *p_old_positions;
1810 uint32_t *p_fribidi_string;
1811 int start_pos, pos = 0;
1813 pp_char_styles_new = (ft_style_t **)
1814 malloc( i_len * sizeof( ft_style_t * ));
1816 p_fribidi_string = (uint32_t *)
1817 malloc( (i_len + 1) * sizeof(uint32_t) );
1818 p_old_positions = (int *)
1819 malloc( (i_len + 1) * sizeof( int ) );
1820 p_new_positions = (int *)
1821 malloc( (i_len + 1) * sizeof( int ) );
1822 p_levels = (int8_t *)
1823 malloc( (i_len + 1) * sizeof( int8_t ) );
1825 if( ! pp_char_styles_new ||
1826 ! p_fribidi_string ||
1827 ! p_old_positions ||
1828 ! p_new_positions ||
1832 free( p_old_positions );
1833 free( p_new_positions );
1834 free( p_fribidi_string );
1835 free( pp_char_styles_new );
1836 free( pi_karaoke_bar );
1838 free( pp_char_styles );
1842 /* Do bidi conversion line-by-line */
1845 while(pos < i_len) {
1846 if (psz_text[pos] != '\n')
1848 p_fribidi_string[pos] = psz_text[pos];
1849 pp_char_styles_new[pos] = pp_char_styles[pos];
1850 p_new_positions[pos] = pos;
1855 while(pos < i_len) {
1856 if (psz_text[pos] == '\n')
1860 if (pos > start_pos)
1862 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1863 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1864 pos - start_pos, &base_dir,
1865 (FriBidiChar*)p_fribidi_string + start_pos,
1866 p_new_positions + start_pos,
1868 p_levels + start_pos );
1869 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1871 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1872 p_old_positions[ j - start_pos ] ];
1873 p_new_positions[ j ] += start_pos;
1877 free( p_old_positions );
1878 free( pp_char_styles );
1879 pp_char_styles = pp_char_styles_new;
1880 psz_text = p_fribidi_string;
1881 p_fribidi_string[ i_len ] = 0;
1884 /* Work out the karaoke */
1885 if( pi_karaoke_bar )
1887 int64_t i_last_duration = 0;
1888 int64_t i_duration = 0;
1889 int64_t i_start_pos = 0;
1890 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1892 for( k = 0; k< i_k_runs; k++ )
1894 double fraction = 0.0;
1896 i_duration += pi_k_durations[ k ];
1898 if( i_duration < i_elapsed )
1900 /* Completely finished this run-length -
1901 * let it render normally */
1905 else if( i_elapsed < i_last_duration )
1907 /* Haven't got up to this segment yet -
1908 * render it completely in karaoke BG mode */
1914 /* Partway through this run */
1916 fraction = (double)(i_elapsed - i_last_duration) /
1917 (double)pi_k_durations[ k ];
1919 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1921 double shade = pi_k_run_lengths[ k ] * fraction;
1923 if( p_new_positions )
1924 j = p_new_positions[ i_start_pos + i ];
1926 j = i_start_pos + i;
1928 if( i < (uint32_t)shade )
1929 pi_karaoke_bar[ j ] = 0xff;
1930 else if( (double)i > shade )
1931 pi_karaoke_bar[ j ] = 0x00;
1934 shade -= (int)shade;
1935 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1936 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1940 i_last_duration = i_duration;
1941 i_start_pos += pi_k_run_lengths[ k ];
1945 free( p_new_positions );
1947 FT_Vector tmp_result;
1949 line_desc_t *p_line = NULL;
1950 line_desc_t *p_prev = NULL;
1956 p_result->x = p_result->y = 0;
1957 tmp_result.x = tmp_result.y = 0;
1960 for( k = 0; k <= (uint32_t) i_len; k++ )
1962 if( ( k == (uint32_t) i_len ) ||
1964 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1966 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1968 /* End of the current style run */
1969 FT_Face p_face = NULL;
1972 /* Look for a match amongst our attachments first */
1973 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1977 char *psz_fontfile = NULL;
1979 psz_fontfile = FontConfig_Select( NULL,
1980 p_style->psz_fontname,
1984 if( psz_fontfile && ! *psz_fontfile )
1986 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1987 " so using default font", p_style->psz_fontname,
1988 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1989 (p_style->b_bold ? "(Bold)" :
1990 (p_style->b_italic ? "(Italic)" : ""))) );
1991 free( psz_fontfile );
1992 psz_fontfile = NULL;
1997 if( FT_New_Face( p_sys->p_library,
1998 psz_fontfile, i_idx, &p_face ) )
2000 free( psz_fontfile );
2001 free( pp_char_styles );
2002 #if defined(HAVE_FRIBIDI)
2005 free( pi_karaoke_bar );
2006 return VLC_EGENERIC;
2008 free( psz_fontfile );
2012 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2014 /* We've loaded a font face which is unhelpful for actually
2015 * rendering text - fallback to the default one.
2017 FT_Done_Face( p_face );
2021 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2022 ft_encoding_unicode ) ||
2023 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2024 p_style->i_font_size ) )
2026 if( p_face ) FT_Done_Face( p_face );
2027 free( pp_char_styles );
2028 #if defined(HAVE_FRIBIDI)
2031 free( pi_karaoke_bar );
2032 return VLC_EGENERIC;
2034 p_sys->i_use_kerning =
2035 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2038 uint32_t *psz_unicode = (uint32_t *)
2039 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2042 if( p_face ) FT_Done_Face( p_face );
2043 free( pp_char_styles );
2044 free( psz_unicode );
2045 #if defined(HAVE_FRIBIDI)
2048 free( pi_karaoke_bar );
2051 memcpy( psz_unicode, psz_text + i_prev,
2052 sizeof( uint32_t ) * ( k - i_prev ) );
2053 psz_unicode[ k - i_prev ] = 0;
2054 while( *psz_unicode )
2058 if( !(p_line = NewLine( i_len - i_prev)) )
2060 if( p_face ) FT_Done_Face( p_face );
2061 free( pp_char_styles );
2062 free( psz_unicode );
2063 #if defined(HAVE_FRIBIDI)
2066 free( pi_karaoke_bar );
2069 /* New Color mode only works in YUVA rendering mode --
2070 * (RGB mode has palette constraints on it). We therefore
2071 * need to populate the legacy colour fields also.
2073 p_line->b_new_color_mode = true;
2074 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2075 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2076 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2077 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2078 p_line->p_next = NULL;
2080 i_pen_y += tmp_result.y;
2084 if( p_prev ) p_prev->p_next = p_line;
2085 else *pp_lines = p_line;
2088 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2089 p_style->i_font_color, p_style->b_underline,
2090 p_style->i_karaoke_bg_color,
2091 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2092 &tmp_result ) != VLC_SUCCESS )
2094 if( p_face ) FT_Done_Face( p_face );
2095 free( pp_char_styles );
2096 free( psz_unicode );
2097 #if defined(HAVE_FRIBIDI)
2100 free( pi_karaoke_bar );
2101 return VLC_EGENERIC;
2106 p_result->x = __MAX( p_result->x, tmp_result.x );
2107 p_result->y += tmp_result.y;
2112 if( *psz_unicode == '\n')
2116 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2118 *c_ptr = *(c_ptr+1);
2123 free( psz_unicode );
2124 if( p_face ) FT_Done_Face( p_face );
2128 free( pp_char_styles );
2129 #if defined(HAVE_FRIBIDI)
2134 p_result->x = __MAX( p_result->x, tmp_result.x );
2135 p_result->y += tmp_result.y;
2138 if( pi_karaoke_bar )
2141 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2143 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2145 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2149 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2151 /* 100% BG colour will render faster if we
2152 * instead make it 100% FG colour, so leave
2153 * the ratio alone and copy the value across
2155 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2159 if( pi_karaoke_bar[ i ] & 0x80 )
2161 /* Swap Left and Right sides over for Right aligned
2162 * language text (eg. Arabic, Hebrew)
2164 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2166 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2167 p_line->p_bg_rgb[ k ] = i_tmp;
2169 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2172 /* Jump over the '\n' at the line-end */
2175 free( pi_karaoke_bar );
2181 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2182 subpicture_region_t *p_region_in )
2184 int rv = VLC_SUCCESS;
2185 stream_t *p_sub = NULL;
2186 xml_reader_t *p_xml_reader = NULL;
2188 if( !p_region_in || !p_region_in->psz_html )
2189 return VLC_EGENERIC;
2191 /* Reset the default fontsize in case screen metrics have changed */
2192 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2194 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2195 (uint8_t *) p_region_in->psz_html,
2196 strlen( p_region_in->psz_html ),
2200 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2201 if( p_filter->p_sys->p_xml )
2203 bool b_karaoke = false;
2205 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2208 /* Look for Root Node */
2209 if( xml_ReaderRead( p_xml_reader ) == 1 )
2211 char *psz_node = xml_ReaderName( p_xml_reader );
2213 if( !strcasecmp( "karaoke", psz_node ) )
2215 /* We're going to have to render the text a number
2216 * of times to show the progress marker on the text.
2218 var_SetBool( p_filter, "text-rerender", true );
2221 else if( !strcasecmp( "text", psz_node ) )
2227 /* Only text and karaoke tags are supported */
2228 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2229 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2230 p_xml_reader = NULL;
2242 uint32_t i_runs = 0;
2243 uint32_t i_k_runs = 0;
2244 uint32_t *pi_run_lengths = NULL;
2245 uint32_t *pi_k_run_lengths = NULL;
2246 uint32_t *pi_k_durations = NULL;
2247 ft_style_t **pp_styles = NULL;
2249 line_desc_t *p_lines = NULL;
2251 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2252 sizeof( uint32_t ) );
2257 rv = ProcessNodes( p_filter, p_xml_reader,
2258 p_region_in->p_style, psz_text, &i_len,
2259 &i_runs, &pi_run_lengths, &pp_styles,
2261 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2264 p_region_out->i_x = p_region_in->i_x;
2265 p_region_out->i_y = p_region_in->i_y;
2267 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2269 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2270 pi_run_lengths, pp_styles, &p_lines, &result,
2271 b_karaoke, i_k_runs, pi_k_run_lengths,
2275 for( k=0; k<i_runs; k++)
2276 DeleteStyle( pp_styles[k] );
2278 free( pi_run_lengths );
2281 /* Don't attempt to render text that couldn't be layed out
2284 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2286 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2288 Render( p_filter, p_region_out, p_lines,
2289 result.x, result.y );
2293 RenderYUVA( p_filter, p_region_out, p_lines,
2294 result.x, result.y );
2298 FreeLines( p_lines );
2300 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2303 stream_Delete( p_sub );
2309 static char* FontConfig_Select( FcConfig* priv, const char* family,
2310 bool b_bold, bool b_italic, int *i_idx )
2313 FcPattern *pat, *p_pat;
2317 pat = FcPatternCreate();
2318 if (!pat) return NULL;
2320 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2321 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2322 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2323 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2325 FcDefaultSubstitute( pat );
2327 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2329 FcPatternDestroy( pat );
2333 p_pat = FcFontMatch( priv, pat, &result );
2334 FcPatternDestroy( pat );
2335 if( !p_pat ) return NULL;
2337 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2338 || ( val_b != FcTrue ) )
2340 FcPatternDestroy( p_pat );
2343 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2348 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2350 FcPatternDestroy( p_pat );
2355 if( strcasecmp((const char*)val_s, family ) != 0 )
2356 msg_Warn( p_filter, "fontconfig: selected font family is not"
2357 "the requested one: '%s' != '%s'\n",
2358 (const char*)val_s, family );
2361 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2363 FcPatternDestroy( p_pat );
2367 FcPatternDestroy( p_pat );
2368 return strdup( (const char*)val_s );
2372 static void FreeLine( line_desc_t *p_line )
2375 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2377 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2379 free( p_line->pp_glyphs );
2380 free( p_line->p_glyph_pos );
2381 free( p_line->p_fg_rgb );
2382 free( p_line->p_bg_rgb );
2383 free( p_line->p_fg_bg_ratio );
2384 free( p_line->pi_underline_offset );
2385 free( p_line->pi_underline_thickness );
2389 static void FreeLines( line_desc_t *p_lines )
2391 line_desc_t *p_line, *p_next;
2393 if( !p_lines ) return;
2395 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2397 p_next = p_line->p_next;
2402 static line_desc_t *NewLine( int i_count )
2404 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2406 if( !p_line ) return NULL;
2407 p_line->i_height = 0;
2408 p_line->i_width = 0;
2409 p_line->p_next = NULL;
2411 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2412 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2413 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2414 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2415 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2416 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2417 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2418 if( ( p_line->pp_glyphs == NULL ) ||
2419 ( p_line->p_glyph_pos == NULL ) ||
2420 ( p_line->p_fg_rgb == NULL ) ||
2421 ( p_line->p_bg_rgb == NULL ) ||
2422 ( p_line->p_fg_bg_ratio == NULL ) ||
2423 ( p_line->pi_underline_offset == NULL ) ||
2424 ( p_line->pi_underline_thickness == NULL ) )
2426 free( p_line->pi_underline_thickness );
2427 free( p_line->pi_underline_offset );
2428 free( p_line->p_fg_rgb );
2429 free( p_line->p_bg_rgb );
2430 free( p_line->p_fg_bg_ratio );
2431 free( p_line->p_glyph_pos );
2432 free( p_line->pp_glyphs );
2436 p_line->pp_glyphs[0] = NULL;
2437 p_line->b_new_color_mode = false;
2442 static int GetFontSize( filter_t *p_filter )
2444 filter_sys_t *p_sys = p_filter->p_sys;
2448 if( p_sys->i_default_font_size )
2450 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2451 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2453 i_size = p_sys->i_default_font_size;
2457 var_Get( p_filter, "freetype-rel-fontsize", &val );
2460 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2461 p_filter->p_sys->i_display_height =
2462 p_filter->fmt_out.video.i_height;
2467 msg_Warn( p_filter, "invalid fontsize, using 12" );
2468 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2469 i_size = 12 * val.i_int / 1000;
2476 static int SetFontSize( filter_t *p_filter, int i_size )
2478 filter_sys_t *p_sys = p_filter->p_sys;
2482 i_size = GetFontSize( p_filter );
2484 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2487 p_sys->i_font_size = i_size;
2489 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2491 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2492 return VLC_EGENERIC;
2498 static void YUVFromRGB( uint32_t i_argb,
2499 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2501 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2502 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2503 int i_blue = ( i_argb & 0x000000ff );
2505 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2506 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2507 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2508 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2509 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2510 -585 * i_blue + 4096 + 1048576) >> 13, 240);