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 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
42 #include <vlc_input.h>
43 #include <vlc_strings.h>
44 #include <vlc_dialog.h>
45 #include <vlc_memory.h>
51 #include FT_FREETYPE_H
53 #define FT_FLOOR(X) ((X & -64) >> 6)
54 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
56 #define FT_MulFix(v, s) (((v)*(s))>>16)
60 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
61 #define FC_DEFAULT_FONT "Arial Black"
62 #elif defined( SYS_BEOS )
63 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
64 #define FC_DEFAULT_FONT "Swiss"
65 #elif defined( WIN32 )
66 #define DEFAULT_FONT "" /* Default font found at run-time */
67 #define FC_DEFAULT_FONT "Arial"
68 #elif defined( HAVE_MAEMO )
69 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
70 #define FC_DEFAULT_FONT "Nokia Sans Bold"
72 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
73 #define FC_DEFAULT_FONT "Serif Bold"
76 #if defined(HAVE_FRIBIDI)
77 #include <fribidi/fribidi.h>
80 #ifdef HAVE_FONTCONFIG
81 #include <fontconfig/fontconfig.h>
83 #define DEFAULT_FONT FC_DEFAULT_FONT
88 /*****************************************************************************
90 *****************************************************************************/
91 static int Create ( vlc_object_t * );
92 static void Destroy( vlc_object_t * );
94 #define FONT_TEXT N_("Font")
96 #ifdef HAVE_FONTCONFIG
97 #define FONT_LONGTEXT N_("Font family for the font you want to use")
99 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
102 #define FONTSIZE_TEXT N_("Font size in pixels")
103 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
104 "that will be rendered on the video. " \
105 "If set to something different than 0 this option will override the " \
106 "relative font size." )
107 #define OPACITY_TEXT N_("Opacity")
108 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
109 "text that will be rendered on the video. 0 = transparent, " \
110 "255 = totally opaque. " )
111 #define COLOR_TEXT N_("Text default color")
112 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
113 "the video. This must be an hexadecimal (like HTML colors). The first two "\
114 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
115 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
116 #define FONTSIZER_TEXT N_("Relative font size")
117 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
118 "fonts that will be rendered on the video. If absolute font size is set, "\
119 "relative size will be overriden." )
121 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
122 static const char *const ppsz_sizes_text[] = {
123 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
124 #define YUVP_TEXT N_("Use YUVP renderer")
125 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
126 "This option is only needed if you want to encode into DVB subtitles" )
127 #define EFFECT_TEXT N_("Font Effect")
128 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
129 "text to improve its readability." )
131 #define EFFECT_BACKGROUND 1
132 #define EFFECT_OUTLINE 2
133 #define EFFECT_OUTLINE_FAT 3
135 static int const pi_effects[] = { 1, 2, 3 };
136 static const char *const ppsz_effects_text[] = {
137 N_("Background"),N_("Outline"), N_("Fat Outline") };
138 static const int pi_color_values[] = {
139 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
140 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
141 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
143 static const char *const ppsz_color_descriptions[] = {
144 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
145 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
146 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
149 set_shortname( N_("Text renderer"))
150 set_description( N_("Freetype2 font renderer") )
151 set_category( CAT_VIDEO )
152 set_subcategory( SUBCAT_VIDEO_SUBPIC )
154 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
157 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
158 FONTSIZE_LONGTEXT, true )
160 /* opacity valid on 0..255, with default 255 = fully opaque */
161 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
162 OPACITY_TEXT, OPACITY_LONGTEXT, true )
164 /* hook to the color values list, with default 0x00ffffff = white */
165 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
166 COLOR_LONGTEXT, false )
167 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
169 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
170 FONTSIZER_LONGTEXT, false )
171 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
172 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
173 EFFECT_LONGTEXT, false )
174 change_integer_list( pi_effects, ppsz_effects_text, NULL )
176 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
177 YUVP_LONGTEXT, true )
178 set_capability( "text renderer", 100 )
179 add_shortcut( "text" )
180 set_callbacks( Create, Destroy )
185 /*****************************************************************************
187 *****************************************************************************/
189 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
190 static int RenderText( filter_t *, subpicture_region_t *,
191 subpicture_region_t * );
192 #ifdef HAVE_FONTCONFIG
193 static int RenderHtml( filter_t *, subpicture_region_t *,
194 subpicture_region_t * );
195 static char *FontConfig_Select( FcConfig *, const char *,
200 static int LoadFontsFromAttachments( filter_t *p_filter );
202 static int GetFontSize( filter_t *p_filter );
203 static int SetFontSize( filter_t *, int );
204 static void YUVFromRGB( uint32_t i_argb,
205 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
207 typedef struct line_desc_t line_desc_t;
210 /** NULL-terminated list of glyphs making the string */
211 FT_BitmapGlyph *pp_glyphs;
212 /** list of relative positions for the glyphs */
213 FT_Vector *p_glyph_pos;
214 /** list of RGB information for styled text
215 * -- if the rendering mode supports it (RenderYUVA) and
216 * b_new_color_mode is set, then it becomes possible to
217 * have multicoloured text within the subtitles. */
220 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
221 bool b_new_color_mode;
222 /** underline information -- only supplied if text should be underlined */
223 uint16_t *pi_underline_offset;
224 uint16_t *pi_underline_thickness;
228 int i_red, i_green, i_blue;
233 static line_desc_t *NewLine( int );
238 uint32_t i_font_color; /* ARGB */
239 uint32_t i_karaoke_bg_color; /* ARGB */
246 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
247 static void FreeLines( line_desc_t * );
248 static void FreeLine( line_desc_t * );
250 /*****************************************************************************
251 * filter_sys_t: freetype local data
252 *****************************************************************************
253 * This structure is part of the video output thread descriptor.
254 * It describes the freetype specific properties of an output thread.
255 *****************************************************************************/
258 FT_Library p_library; /* handle to library */
259 FT_Face p_face; /* handle to face object */
261 uint8_t i_font_opacity;
266 int i_default_font_size;
267 int i_display_height;
268 #ifdef HAVE_FONTCONFIG
269 char* psz_fontfamily;
273 input_attachment_t **pp_font_attachments;
274 int i_font_attachments;
278 #define UCHAR uint32_t
279 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
280 #define TR_FONT_STYLE_PTR ft_style_t *
282 #include "text_renderer.h"
284 /*****************************************************************************
285 * Create: allocates osd-text video thread output method
286 *****************************************************************************
287 * This function allocates and initializes a Clone vout method.
288 *****************************************************************************/
289 static int Create( vlc_object_t *p_this )
291 filter_t *p_filter = (filter_t *)p_this;
293 char *psz_fontfile=NULL;
294 char *psz_fontfamily=NULL;
295 int i_error,fontindex;
297 #ifdef HAVE_FONTCONFIG
298 FcPattern *fontpattern = NULL, *fontmatch = NULL;
299 /* Initialise result to Match, as fontconfig doesnt
300 * really set this other than some error-cases */
301 FcResult fontresult = FcResultMatch;
305 /* Allocate structure */
306 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
309 #ifdef HAVE_FONTCONFIG
310 p_sys->psz_fontfamily = NULL;
314 p_sys->p_library = 0;
315 p_sys->i_font_size = 0;
316 p_sys->i_display_height = 0;
318 var_Create( p_filter, "freetype-rel-fontsize",
319 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
321 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
322 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
323 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
324 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
325 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
326 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
327 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
330 if( !psz_fontfamily || !*psz_fontfamily )
332 free( psz_fontfamily );
333 #ifdef HAVE_FONTCONFIG
334 psz_fontfamily=strdup( DEFAULT_FONT );
336 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
337 if( !psz_fontfamily )
340 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
341 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
343 strcpy( psz_fontfamily, DEFAULT_FONT );
345 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
349 #ifdef HAVE_FONTCONFIG
350 /* Lets find some fontfile from freetype-font variable family */
352 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
356 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
357 _("Building font cache"),
358 _("Please wait while your font cache is rebuilt.\n"
359 "This should take less than few minutes."), NULL );
361 path = (char *)malloc( PATH_MAX + 1 );
362 /* Fontconfig doesnt seem to know where windows fonts are with
363 * current contribs. So just tell default windows font directory
364 * is the place to search fonts
367 GetWindowsDirectory( path, PATH_MAX + 1 );
368 strcat( path, "\\fonts" );
370 dialog_ProgressSet( p_dialog, NULL, 0.4 );
372 FcConfigAppFontAddDir( NULL , path );
377 dialog_ProgressSet( p_dialog, NULL, 0.5 );
381 msg_Dbg( p_filter, "Building font database.");
383 FcConfigBuildFonts( NULL );
386 msg_Dbg( p_filter, "Finished building font database." );
387 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
389 fontpattern = FcPatternCreate();
393 msg_Err( p_filter, "Creating fontpattern failed");
399 dialog_ProgressSet( p_dialog, NULL, 0.7 );
401 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
402 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
403 free( psz_fontsize );
405 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
407 msg_Err( p_filter, "FontSubstitute failed");
410 FcDefaultSubstitute( fontpattern );
414 dialog_ProgressSet( p_dialog, NULL, 0.8 );
416 /* testing fontresult here doesn't do any good really, but maybe it will
417 * in future as fontconfig code doesn't set it in all cases and just
418 * returns NULL or doesn't set to to Match on all Match cases.*/
419 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
420 if( !fontmatch || fontresult == FcResultNoMatch )
422 msg_Err( p_filter, "Fontmatching failed");
426 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
427 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
430 msg_Err( p_filter, "Failed to get fontfile");
434 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
435 p_sys->psz_fontfamily = strdup( psz_fontfamily );
439 dialog_ProgressSet( p_dialog, NULL, 1.0 );
440 dialog_ProgressDestroy( p_dialog );
446 #ifdef HAVE_FONTCONFIG
447 p_sys->psz_fontfamily = strdup( DEFAULT_FONT );
448 psz_fontfile = psz_fontfamily;
453 i_error = FT_Init_FreeType( &p_sys->p_library );
456 msg_Err( p_filter, "couldn't initialize freetype" );
460 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
461 fontindex, &p_sys->p_face );
463 if( i_error == FT_Err_Unknown_File_Format )
465 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
470 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
474 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
477 msg_Err( p_filter, "font has no unicode translation table" );
481 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
483 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
486 p_sys->pp_font_attachments = NULL;
487 p_sys->i_font_attachments = 0;
489 p_filter->pf_render_text = RenderText;
490 #ifdef HAVE_FONTCONFIG
491 p_filter->pf_render_html = RenderHtml;
492 FcPatternDestroy( fontmatch );
493 FcPatternDestroy( fontpattern );
495 p_filter->pf_render_html = NULL;
498 free( psz_fontfamily );
499 LoadFontsFromAttachments( p_filter );
504 #ifdef HAVE_FONTCONFIG
505 if( fontmatch ) FcPatternDestroy( fontmatch );
506 if( fontpattern ) FcPatternDestroy( fontpattern );
508 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
509 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
510 free( psz_fontfamily );
515 /*****************************************************************************
516 * Destroy: destroy Clone video thread output method
517 *****************************************************************************
518 * Clean up all data and library connections
519 *****************************************************************************/
520 static void Destroy( vlc_object_t *p_this )
522 filter_t *p_filter = (filter_t *)p_this;
523 filter_sys_t *p_sys = p_filter->p_sys;
525 if( p_sys->pp_font_attachments )
529 for( k = 0; k < p_sys->i_font_attachments; k++ )
530 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
532 free( p_sys->pp_font_attachments );
535 #ifdef HAVE_FONTCONFIG
536 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
537 free( p_sys->psz_fontfamily );
540 /* FcFini asserts calling the subfunction FcCacheFini()
541 * even if no other library functions have been made since FcInit(),
542 * so don't call it. */
544 FT_Done_Face( p_sys->p_face );
545 FT_Done_FreeType( p_sys->p_library );
549 /*****************************************************************************
550 * Make any TTF/OTF fonts present in the attachments of the media file
551 * and store them for later use by the FreeType Engine
552 *****************************************************************************/
553 static int LoadFontsFromAttachments( filter_t *p_filter )
555 filter_sys_t *p_sys = p_filter->p_sys;
556 input_thread_t *p_input;
557 input_attachment_t **pp_attachments;
558 int i_attachments_cnt;
560 int rv = VLC_SUCCESS;
562 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
566 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
568 vlc_object_release(p_input);
572 p_sys->i_font_attachments = 0;
573 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
574 if(! p_sys->pp_font_attachments )
577 for( k = 0; k < i_attachments_cnt; k++ )
579 input_attachment_t *p_attach = pp_attachments[k];
581 if( p_sys->pp_font_attachments )
583 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
584 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
585 ( p_attach->i_data > 0 ) &&
586 ( p_attach->p_data != NULL ) )
588 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
592 vlc_input_attachment_Delete( p_attach );
597 vlc_input_attachment_Delete( p_attach );
600 free( pp_attachments );
602 vlc_object_release(p_input);
607 /*****************************************************************************
608 * Render: place string in picture
609 *****************************************************************************
610 * This function merges the previously rendered freetype glyphs into a picture
611 *****************************************************************************/
612 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
613 line_desc_t *p_line, int i_width, int i_height )
615 VLC_UNUSED(p_filter);
616 static const uint8_t pi_gamma[16] =
617 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
618 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
622 int i, x, y, i_pitch;
623 uint8_t i_y; /* YUV values, derived from incoming RGB */
626 /* Create a new subpicture region */
627 memset( &fmt, 0, sizeof(video_format_t) );
628 fmt.i_chroma = VLC_CODEC_YUVP;
630 fmt.i_width = fmt.i_visible_width = i_width + 4;
631 fmt.i_height = fmt.i_visible_height = i_height + 4;
632 if( p_region->fmt.i_visible_width > 0 )
633 fmt.i_visible_width = p_region->fmt.i_visible_width;
634 if( p_region->fmt.i_visible_height > 0 )
635 fmt.i_visible_height = p_region->fmt.i_visible_height;
636 fmt.i_x_offset = fmt.i_y_offset = 0;
638 assert( !p_region->p_picture );
639 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
640 if( !p_region->p_picture )
644 /* Calculate text color components */
645 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
646 25 * p_line->i_blue + 128) >> 8) + 16;
647 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
648 112 * p_line->i_blue + 128) >> 8) + 128;
649 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
650 18 * p_line->i_blue + 128) >> 8) + 128;
653 fmt.p_palette->i_entries = 16;
654 for( i = 0; i < 8; i++ )
656 fmt.p_palette->palette[i][0] = 0;
657 fmt.p_palette->palette[i][1] = 0x80;
658 fmt.p_palette->palette[i][2] = 0x80;
659 fmt.p_palette->palette[i][3] = pi_gamma[i];
660 fmt.p_palette->palette[i][3] =
661 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
663 for( i = 8; i < fmt.p_palette->i_entries; i++ )
665 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
666 fmt.p_palette->palette[i][1] = i_u;
667 fmt.p_palette->palette[i][2] = i_v;
668 fmt.p_palette->palette[i][3] = pi_gamma[i];
669 fmt.p_palette->palette[i][3] =
670 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
673 p_dst = p_region->p_picture->Y_PIXELS;
674 i_pitch = p_region->p_picture->Y_PITCH;
676 /* Initialize the region pixels */
677 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
679 for( ; p_line != NULL; p_line = p_line->p_next )
681 int i_glyph_tmax = 0;
682 int i_bitmap_offset, i_offset, i_align_offset = 0;
683 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
685 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
686 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
689 if( p_line->i_width < i_width )
691 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
693 i_align_offset = i_width - p_line->i_width;
695 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
697 i_align_offset = ( i_width - p_line->i_width ) / 2;
701 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
703 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
705 i_offset = ( p_line->p_glyph_pos[ i ].y +
706 i_glyph_tmax - p_glyph->top + 2 ) *
707 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
710 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
712 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
714 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
716 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
723 /* Outlining (find something better than nearest neighbour filtering ?) */
726 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
727 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
728 uint8_t left, current;
730 for( y = 1; y < (int)fmt.i_height - 1; y++ )
732 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
733 p_dst += p_region->p_picture->Y_PITCH;
736 for( x = 1; x < (int)fmt.i_width - 1; x++ )
739 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
740 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;
744 memset( p_top, 0, fmt.i_width );
750 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
751 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
752 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
753 int i_glyph_tmax, int i_align_offset,
754 uint8_t i_y, uint8_t i_u, uint8_t i_v,
755 subpicture_region_t *p_region)
759 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
761 p_dst_y = p_region->p_picture->Y_PIXELS;
762 p_dst_u = p_region->p_picture->U_PIXELS;
763 p_dst_v = p_region->p_picture->V_PIXELS;
764 p_dst_a = p_region->p_picture->A_PIXELS;
765 i_pitch = p_region->p_picture->A_PITCH;
767 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
768 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
770 for( y = 0; y < i_line_thickness; y++ )
772 int i_extra = p_this_glyph->bitmap.width;
776 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
777 (p_this_glyph_pos->x + p_this_glyph->left);
779 for( x = 0; x < i_extra; x++ )
783 /* break the underline around the tails of any glyphs which cross it */
784 for( z = x - i_line_thickness;
785 z < x + i_line_thickness && b_ok;
788 if( p_next_glyph && ( z >= i_extra ) )
790 int i_row = i_line_offset + p_next_glyph->top + y;
792 if( ( p_next_glyph->bitmap.rows > i_row ) &&
793 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
798 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
800 int i_row = i_line_offset + p_this_glyph->top + y;
802 if( ( p_this_glyph->bitmap.rows > i_row ) &&
803 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
812 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
813 p_dst_u[i_offset+x] = i_u;
814 p_dst_v[i_offset+x] = i_v;
815 p_dst_a[i_offset+x] = 255;
822 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
824 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
825 int i_pitch = p_region->p_picture->A_PITCH;
828 for( ; p_line != NULL; p_line = p_line->p_next )
830 int i_glyph_tmax=0, i = 0;
831 int i_bitmap_offset, i_offset, i_align_offset = 0;
832 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
834 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
835 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
838 if( p_line->i_width < i_width )
840 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
842 i_align_offset = i_width - p_line->i_width;
844 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
846 i_align_offset = ( i_width - p_line->i_width ) / 2;
850 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
852 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
854 i_offset = ( p_line->p_glyph_pos[ i ].y +
855 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
856 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
857 i_align_offset +xoffset;
859 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
861 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
863 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
864 if( p_dst[i_offset+x] <
865 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
867 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
876 /*****************************************************************************
877 * Render: place string in picture
878 *****************************************************************************
879 * This function merges the previously rendered freetype glyphs into a picture
880 *****************************************************************************/
881 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
882 line_desc_t *p_line, int i_width, int i_height )
884 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
886 int i, x, y, i_pitch, i_alpha;
887 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
889 if( i_width == 0 || i_height == 0 )
892 /* Create a new subpicture region */
893 memset( &fmt, 0, sizeof(video_format_t) );
894 fmt.i_chroma = VLC_CODEC_YUVA;
896 fmt.i_width = fmt.i_visible_width = i_width + 6;
897 fmt.i_height = fmt.i_visible_height = i_height + 6;
898 if( p_region->fmt.i_visible_width > 0 )
899 fmt.i_visible_width = p_region->fmt.i_visible_width;
900 if( p_region->fmt.i_visible_height > 0 )
901 fmt.i_visible_height = p_region->fmt.i_visible_height;
902 fmt.i_x_offset = fmt.i_y_offset = 0;
904 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
905 if( !p_region->p_picture )
909 /* Calculate text color components */
910 YUVFromRGB( (p_line->i_red << 16) |
911 (p_line->i_green << 8) |
914 i_alpha = p_line->i_alpha;
916 p_dst_y = p_region->p_picture->Y_PIXELS;
917 p_dst_u = p_region->p_picture->U_PIXELS;
918 p_dst_v = p_region->p_picture->V_PIXELS;
919 p_dst_a = p_region->p_picture->A_PIXELS;
920 i_pitch = p_region->p_picture->A_PITCH;
922 /* Initialize the region pixels */
923 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
925 memset( p_dst_y, 0x00, 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, 0, i_pitch * p_region->fmt.i_height );
932 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
933 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
934 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
935 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
937 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
938 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
940 DrawBlack( p_line, i_width, p_region, 0, 0);
941 DrawBlack( p_line, i_width, p_region, -1, 0);
942 DrawBlack( p_line, i_width, p_region, 0, -1);
943 DrawBlack( p_line, i_width, p_region, 1, 0);
944 DrawBlack( p_line, i_width, p_region, 0, 1);
947 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
949 DrawBlack( p_line, i_width, p_region, -1, -1);
950 DrawBlack( p_line, i_width, p_region, -1, 1);
951 DrawBlack( p_line, i_width, p_region, 1, -1);
952 DrawBlack( p_line, i_width, p_region, 1, 1);
954 DrawBlack( p_line, i_width, p_region, -2, 0);
955 DrawBlack( p_line, i_width, p_region, 0, -2);
956 DrawBlack( p_line, i_width, p_region, 2, 0);
957 DrawBlack( p_line, i_width, p_region, 0, 2);
959 DrawBlack( p_line, i_width, p_region, -2, -2);
960 DrawBlack( p_line, i_width, p_region, -2, 2);
961 DrawBlack( p_line, i_width, p_region, 2, -2);
962 DrawBlack( p_line, i_width, p_region, 2, 2);
964 DrawBlack( p_line, i_width, p_region, -3, 0);
965 DrawBlack( p_line, i_width, p_region, 0, -3);
966 DrawBlack( p_line, i_width, p_region, 3, 0);
967 DrawBlack( p_line, i_width, p_region, 0, 3);
970 for( ; p_line != NULL; p_line = p_line->p_next )
972 int i_glyph_tmax = 0;
973 int i_bitmap_offset, i_offset, i_align_offset = 0;
974 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
976 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
977 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
980 if( p_line->i_width < i_width )
982 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
984 i_align_offset = i_width - p_line->i_width;
986 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
988 i_align_offset = ( i_width - p_line->i_width ) / 2;
992 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
994 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
996 i_offset = ( p_line->p_glyph_pos[ i ].y +
997 i_glyph_tmax - p_glyph->top + 3 ) *
998 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
1001 if( p_line->b_new_color_mode )
1003 /* Every glyph can (and in fact must) have its own color */
1004 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
1007 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
1009 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
1011 uint8_t i_y_local = i_y;
1012 uint8_t i_u_local = i_u;
1013 uint8_t i_v_local = i_v;
1015 if( p_line->p_fg_bg_ratio != 0x00 )
1017 int i_split = p_glyph->bitmap.width *
1018 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1022 YUVFromRGB( p_line->p_bg_rgb[ i ],
1023 &i_y_local, &i_u_local, &i_v_local );
1027 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1029 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1030 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1032 p_dst_u[i_offset+x] = i_u;
1033 p_dst_v[i_offset+x] = i_v;
1035 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1036 p_dst_a[i_offset+x] = 0xff;
1039 i_offset += i_pitch;
1042 if( p_line->pi_underline_thickness[ i ] )
1044 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1045 p_line->pi_underline_offset[ i ],
1046 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1047 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1048 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1049 i_glyph_tmax, i_align_offset,
1056 /* Apply the alpha setting */
1057 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1058 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1064 * This function renders a text subpicture region into another one.
1065 * It also calculates the size needed for this string, and renders the
1066 * needed glyphs into memory. It is used as pf_add_string callback in
1067 * the vout method by this module
1069 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1070 subpicture_region_t *p_region_in )
1072 filter_sys_t *p_sys = p_filter->p_sys;
1073 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1074 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1075 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1076 int i_string_length;
1078 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1079 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1089 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1090 psz_string = p_region_in->psz_text;
1091 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1093 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1094 i_scale = val.i_int;
1096 if( p_region_in->p_style )
1098 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1099 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1100 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1104 i_font_color = p_sys->i_font_color;
1105 i_font_alpha = 255 - p_sys->i_font_opacity;
1106 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1109 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1110 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1111 SetFontSize( p_filter, i_font_size );
1113 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1114 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1115 i_blue = i_font_color & 0x000000FF;
1117 result.x = result.y = 0;
1118 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1120 psz_unicode = psz_unicode_orig =
1121 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1122 if( psz_unicode == NULL )
1124 #if defined(WORDS_BIGENDIAN)
1125 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1127 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1129 if( iconv_handle == (vlc_iconv_t)-1 )
1131 msg_Warn( p_filter, "unable to do conversion" );
1137 const char *p_in_buffer = psz_string;
1138 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1139 i_in_bytes = strlen( psz_string );
1140 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1141 i_out_bytes_left = i_out_bytes;
1142 p_out_buffer = (char *)psz_unicode;
1143 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1145 &p_out_buffer, &i_out_bytes_left );
1147 vlc_iconv_close( iconv_handle );
1151 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1152 "bytes left %u", (unsigned)i_in_bytes );
1155 *(uint32_t*)p_out_buffer = 0;
1156 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1159 #if defined(HAVE_FRIBIDI)
1161 uint32_t *p_fribidi_string;
1162 int32_t start_pos, pos = 0;
1164 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1165 if( !p_fribidi_string )
1168 /* Do bidi conversion line-by-line */
1169 while( pos < i_string_length )
1171 while( pos < i_string_length )
1173 i_char = psz_unicode[pos];
1174 if (i_char != '\r' && i_char != '\n')
1176 p_fribidi_string[pos] = i_char;
1180 while( pos < i_string_length )
1182 i_char = psz_unicode[pos];
1183 if (i_char == '\r' || i_char == '\n')
1187 if (pos > start_pos)
1189 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1190 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1193 (FriBidiChar*)p_fribidi_string + start_pos,
1198 free( psz_unicode_orig );
1199 psz_unicode = psz_unicode_orig = p_fribidi_string;
1200 p_fribidi_string[ i_string_length ] = 0;
1204 /* Calculate relative glyph positions and a bounding box for the
1206 if( !(p_line = NewLine( strlen( psz_string ))) )
1209 i_pen_x = i_pen_y = 0;
1211 psz_line_start = psz_unicode;
1213 #define face p_sys->p_face
1214 #define glyph face->glyph
1216 while( *psz_unicode )
1218 i_char = *psz_unicode++;
1219 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1224 if( i_char == '\n' )
1226 psz_line_start = psz_unicode;
1227 if( !(p_next = NewLine( strlen( psz_string ))) )
1229 p_line->p_next = p_next;
1230 p_line->i_width = line.xMax;
1231 p_line->i_height = face->size->metrics.height >> 6;
1232 p_line->pp_glyphs[ i ] = NULL;
1233 p_line->i_alpha = i_font_alpha;
1234 p_line->i_red = i_red;
1235 p_line->i_green = i_green;
1236 p_line->i_blue = i_blue;
1239 result.x = __MAX( result.x, line.xMax );
1240 result.y += face->size->metrics.height >> 6;
1243 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1244 i_pen_y += face->size->metrics.height >> 6;
1246 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1251 i_glyph_index = FT_Get_Char_Index( face, i_char );
1252 if( p_sys->i_use_kerning && i_glyph_index
1256 FT_Get_Kerning( face, i_previous, i_glyph_index,
1257 ft_kerning_default, &delta );
1258 i_pen_x += delta.x >> 6;
1261 p_line->p_glyph_pos[ i ].x = i_pen_x;
1262 p_line->p_glyph_pos[ i ].y = i_pen_y;
1263 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1266 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1270 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1273 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1277 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1278 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1281 FT_Done_Glyph( tmp_glyph );
1284 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1287 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1288 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1289 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1291 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1292 p_line->pp_glyphs[ i ] = NULL;
1294 p_line = NewLine( strlen( psz_string ));
1295 if( p_prev ) p_prev->p_next = p_line;
1296 else p_lines = p_line;
1298 uint32_t *psz_unicode_saved = psz_unicode;
1299 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1303 if( psz_unicode == psz_line_start )
1304 { /* try harder to break that line */
1305 psz_unicode = psz_unicode_saved;
1306 while( psz_unicode > psz_line_start &&
1307 *psz_unicode != '_' && *psz_unicode != '/' &&
1308 *psz_unicode != '\\' && *psz_unicode != '.' )
1313 if( psz_unicode == psz_line_start )
1315 msg_Warn( p_filter, "unbreakable string" );
1320 *psz_unicode = '\n';
1322 psz_unicode = psz_line_start;
1325 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1328 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1329 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1331 i_previous = i_glyph_index;
1332 i_pen_x += glyph->advance.x >> 6;
1336 p_line->i_width = line.xMax;
1337 p_line->i_height = face->size->metrics.height >> 6;
1338 p_line->pp_glyphs[ i ] = NULL;
1339 p_line->i_alpha = i_font_alpha;
1340 p_line->i_red = i_red;
1341 p_line->i_green = i_green;
1342 p_line->i_blue = i_blue;
1343 result.x = __MAX( result.x, line.xMax );
1344 result.y += line.yMax - line.yMin;
1349 p_region_out->i_x = p_region_in->i_x;
1350 p_region_out->i_y = p_region_in->i_y;
1352 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1353 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1355 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1357 free( psz_unicode_orig );
1358 FreeLines( p_lines );
1362 free( psz_unicode_orig );
1363 FreeLines( p_lines );
1364 return VLC_EGENERIC;
1367 #ifdef HAVE_FONTCONFIG
1368 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1369 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1370 bool b_italic, bool b_uline )
1372 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1376 p_style->i_font_size = i_font_size;
1377 p_style->i_font_color = i_font_color;
1378 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1379 p_style->b_italic = b_italic;
1380 p_style->b_bold = b_bold;
1381 p_style->b_underline = b_uline;
1383 p_style->psz_fontname = strdup( psz_fontname );
1388 static void DeleteStyle( ft_style_t *p_style )
1392 free( p_style->psz_fontname );
1397 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1404 if(( s1->i_font_size == s2->i_font_size ) &&
1405 ( s1->i_font_color == s2->i_font_color ) &&
1406 ( s1->b_italic == s2->b_italic ) &&
1407 ( s1->b_bold == s2->b_bold ) &&
1408 ( s1->b_underline == s2->b_underline ) &&
1409 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1416 static void IconvText( filter_t *p_filter, const char *psz_string,
1417 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1419 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1421 /* If memory hasn't been allocated for our output string, allocate it here
1422 * - the calling function must now be responsible for freeing it.
1424 if( !*ppsz_unicode )
1425 *ppsz_unicode = (uint32_t *)
1426 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1428 /* We don't need to handle a NULL pointer in *ppsz_unicode
1429 * if we are instead testing for a non NULL value like we are here */
1433 #if defined(WORDS_BIGENDIAN)
1434 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1436 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1438 if( iconv_handle != (vlc_iconv_t)-1 )
1440 char *p_in_buffer, *p_out_buffer;
1441 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1442 i_in_bytes = strlen( psz_string );
1443 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1444 i_out_bytes_left = i_out_bytes;
1445 p_in_buffer = (char *) psz_string;
1446 p_out_buffer = (char *) *ppsz_unicode;
1447 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1448 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1450 vlc_iconv_close( iconv_handle );
1454 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1455 "bytes left %u", (unsigned)i_in_bytes );
1459 *(uint32_t*)p_out_buffer = 0;
1461 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1466 msg_Warn( p_filter, "unable to do conversion" );
1471 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1472 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1475 ft_style_t *p_style = NULL;
1477 char *psz_fontname = NULL;
1478 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1479 uint32_t i_karaoke_bg_color = i_font_color;
1480 int i_font_size = p_sys->i_font_size;
1482 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1483 &i_font_color, &i_karaoke_bg_color ))
1485 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1486 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1491 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1492 bool b_uline, int i_karaoke_bgcolor,
1493 line_desc_t *p_line, uint32_t *psz_unicode,
1494 int *pi_pen_x, int i_pen_y, int *pi_start,
1495 FT_Vector *p_result )
1500 bool b_first_on_line = true;
1503 int i_pen_x_start = *pi_pen_x;
1505 uint32_t *psz_unicode_start = psz_unicode;
1507 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1509 /* Account for part of line already in position */
1510 for( i=0; i<*pi_start; i++ )
1514 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1515 ft_glyph_bbox_pixels, &glyph_size );
1517 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1518 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1519 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1520 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1526 b_first_on_line = false;
1528 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1534 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1535 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1539 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1540 ft_kerning_default, &delta );
1541 *pi_pen_x += delta.x >> 6;
1543 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1544 p_line->p_glyph_pos[ i ].y = i_pen_y;
1546 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1550 "unable to render text FT_Load_Glyph returned %d", i_error );
1551 p_line->pp_glyphs[ i ] = NULL;
1552 return VLC_EGENERIC;
1554 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1558 "unable to render text FT_Get_Glyph returned %d", i_error );
1559 p_line->pp_glyphs[ i ] = NULL;
1560 return VLC_EGENERIC;
1562 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1563 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1566 FT_Done_Glyph( tmp_glyph );
1571 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1572 p_face->size->metrics.y_scale));
1573 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1574 p_face->size->metrics.y_scale));
1576 p_line->pi_underline_offset[ i ] =
1577 ( aOffset < 0 ) ? -aOffset : aOffset;
1578 p_line->pi_underline_thickness[ i ] =
1579 ( aSize < 0 ) ? -aSize : aSize;
1581 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1582 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1583 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1584 p_line->p_fg_bg_ratio[ i ] = 0x00;
1586 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1587 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1588 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1590 for( ; i >= *pi_start; i-- )
1591 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1594 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1598 if( psz_unicode == psz_unicode_start )
1600 if( b_first_on_line )
1602 msg_Warn( p_filter, "unbreakable string" );
1603 p_line->pp_glyphs[ i ] = NULL;
1604 return VLC_EGENERIC;
1606 *pi_pen_x = i_pen_x_start;
1608 p_line->i_width = line.xMax;
1609 p_line->i_height = __MAX( p_line->i_height,
1610 p_face->size->metrics.height >> 6 );
1611 p_line->pp_glyphs[ i ] = NULL;
1613 p_result->x = __MAX( p_result->x, line.xMax );
1614 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1615 i_yMax - i_yMin ) );
1620 *psz_unicode = '\n';
1622 psz_unicode = psz_unicode_start;
1623 *pi_pen_x = i_pen_x_start;
1631 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1632 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1634 i_previous = i_glyph_index;
1635 *pi_pen_x += p_face->glyph->advance.x >> 6;
1638 p_line->i_width = line.xMax;
1639 p_line->i_height = __MAX( p_line->i_height,
1640 p_face->size->metrics.height >> 6 );
1641 p_line->pp_glyphs[ i ] = NULL;
1643 p_result->x = __MAX( p_result->x, line.xMax );
1644 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1645 line.yMax - line.yMin ) );
1649 /* Get rid of any text processed - if necessary repositioning
1650 * at the start of a new line of text
1654 *psz_unicode_start = '\0';
1656 else if( psz_unicode > psz_unicode_start )
1658 for( i=0; psz_unicode[ i ]; i++ )
1659 psz_unicode_start[ i ] = psz_unicode[ i ];
1660 psz_unicode_start[ i ] = '\0';
1666 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1667 uint32_t **psz_text_out, uint32_t *pi_runs,
1668 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1669 ft_style_t *p_style )
1671 uint32_t i_string_length = 0;
1673 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1674 *psz_text_out += i_string_length;
1676 if( ppp_styles && ppi_run_lengths )
1680 /* XXX this logic looks somewhat broken */
1684 *ppp_styles = realloc_or_free( *ppp_styles,
1685 *pi_runs * sizeof( ft_style_t * ) );
1687 else if( *pi_runs == 1 )
1689 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1692 /* We have just malloc'ed this memory successfully -
1693 * *pi_runs HAS to be within the memory area of *ppp_styles */
1696 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1700 /* XXX more iffy logic */
1702 if( *ppi_run_lengths )
1704 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1705 *pi_runs * sizeof( uint32_t ) );
1707 else if( *pi_runs == 1 )
1709 *ppi_run_lengths = (uint32_t *)
1710 malloc( *pi_runs * sizeof( uint32_t ) );
1713 /* same remarks here */
1714 if( *ppi_run_lengths )
1716 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1719 /* If we couldn't use the p_style argument due to memory allocation
1720 * problems above, release it here.
1722 if( p_style ) DeleteStyle( p_style );
1725 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1729 for( k=0; k < p_sys->i_font_attachments; k++ )
1731 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1733 FT_Face p_face = NULL;
1735 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1743 bool match = !strcasecmp( p_face->family_name,
1744 p_style->psz_fontname );
1746 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1747 match = match && p_style->b_bold;
1749 match = match && !p_style->b_bold;
1751 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1752 match = match && p_style->b_italic;
1754 match = match && !p_style->b_italic;
1762 FT_Done_Face( p_face );
1767 return VLC_EGENERIC;
1770 static int ProcessLines( filter_t *p_filter,
1775 uint32_t *pi_run_lengths,
1776 ft_style_t **pp_styles,
1777 line_desc_t **pp_lines,
1779 FT_Vector *p_result,
1783 uint32_t *pi_k_run_lengths,
1784 uint32_t *pi_k_durations )
1786 filter_sys_t *p_sys = p_filter->p_sys;
1787 ft_style_t **pp_char_styles;
1788 int *p_new_positions = NULL;
1789 int8_t *p_levels = NULL;
1790 uint8_t *pi_karaoke_bar = NULL;
1794 /* Assign each character in the text string its style explicitly, so that
1795 * after the characters have been shuffled around by Fribidi, we can re-apply
1796 * the styles, and to simplify the calculation of runs within a line.
1798 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1799 if( !pp_char_styles )
1804 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1805 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1806 * we just won't be able to display the progress bar; at least we'll
1812 for( j = 0; j < i_runs; j++ )
1813 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1814 pp_char_styles[ i++ ] = pp_styles[ j ];
1816 #if defined(HAVE_FRIBIDI)
1818 ft_style_t **pp_char_styles_new;
1819 int *p_old_positions;
1820 uint32_t *p_fribidi_string;
1821 int start_pos, pos = 0;
1823 pp_char_styles_new = (ft_style_t **)
1824 malloc( i_len * sizeof( ft_style_t * ));
1826 p_fribidi_string = (uint32_t *)
1827 malloc( (i_len + 1) * sizeof(uint32_t) );
1828 p_old_positions = (int *)
1829 malloc( (i_len + 1) * sizeof( int ) );
1830 p_new_positions = (int *)
1831 malloc( (i_len + 1) * sizeof( int ) );
1832 p_levels = (int8_t *)
1833 malloc( (i_len + 1) * sizeof( int8_t ) );
1835 if( ! pp_char_styles_new ||
1836 ! p_fribidi_string ||
1837 ! p_old_positions ||
1838 ! p_new_positions ||
1842 free( p_old_positions );
1843 free( p_new_positions );
1844 free( p_fribidi_string );
1845 free( pp_char_styles_new );
1846 free( pi_karaoke_bar );
1848 free( pp_char_styles );
1852 /* Do bidi conversion line-by-line */
1855 while(pos < i_len) {
1856 if (psz_text[pos] != '\n')
1858 p_fribidi_string[pos] = psz_text[pos];
1859 pp_char_styles_new[pos] = pp_char_styles[pos];
1860 p_new_positions[pos] = pos;
1865 while(pos < i_len) {
1866 if (psz_text[pos] == '\n')
1870 if (pos > start_pos)
1872 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1873 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1874 pos - start_pos, &base_dir,
1875 (FriBidiChar*)p_fribidi_string + start_pos,
1876 p_new_positions + start_pos,
1878 p_levels + start_pos );
1879 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1881 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1882 p_old_positions[ j - start_pos ] ];
1883 p_new_positions[ j ] += start_pos;
1887 free( p_old_positions );
1888 free( pp_char_styles );
1889 pp_char_styles = pp_char_styles_new;
1890 psz_text = p_fribidi_string;
1891 p_fribidi_string[ i_len ] = 0;
1894 /* Work out the karaoke */
1895 if( pi_karaoke_bar )
1897 int64_t i_last_duration = 0;
1898 int64_t i_duration = 0;
1899 int64_t i_start_pos = 0;
1900 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1902 for( k = 0; k< i_k_runs; k++ )
1904 double fraction = 0.0;
1906 i_duration += pi_k_durations[ k ];
1908 if( i_duration < i_elapsed )
1910 /* Completely finished this run-length -
1911 * let it render normally */
1915 else if( i_elapsed < i_last_duration )
1917 /* Haven't got up to this segment yet -
1918 * render it completely in karaoke BG mode */
1924 /* Partway through this run */
1926 fraction = (double)(i_elapsed - i_last_duration) /
1927 (double)pi_k_durations[ k ];
1929 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1931 double shade = pi_k_run_lengths[ k ] * fraction;
1933 if( p_new_positions )
1934 j = p_new_positions[ i_start_pos + i ];
1936 j = i_start_pos + i;
1938 if( i < (uint32_t)shade )
1939 pi_karaoke_bar[ j ] = 0xff;
1940 else if( (double)i > shade )
1941 pi_karaoke_bar[ j ] = 0x00;
1944 shade -= (int)shade;
1945 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1946 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1950 i_last_duration = i_duration;
1951 i_start_pos += pi_k_run_lengths[ k ];
1955 free( p_new_positions );
1957 FT_Vector tmp_result;
1959 line_desc_t *p_line = NULL;
1960 line_desc_t *p_prev = NULL;
1966 p_result->x = p_result->y = 0;
1967 tmp_result.x = tmp_result.y = 0;
1970 for( k = 0; k <= (uint32_t) i_len; k++ )
1972 if( ( k == (uint32_t) i_len ) ||
1974 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1976 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1978 /* End of the current style run */
1979 FT_Face p_face = NULL;
1982 /* Look for a match amongst our attachments first */
1983 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1987 char *psz_fontfile = NULL;
1989 psz_fontfile = FontConfig_Select( NULL,
1990 p_style->psz_fontname,
1994 if( psz_fontfile && ! *psz_fontfile )
1996 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1997 " so using default font", p_style->psz_fontname,
1998 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1999 (p_style->b_bold ? "(Bold)" :
2000 (p_style->b_italic ? "(Italic)" : ""))) );
2001 free( psz_fontfile );
2002 psz_fontfile = NULL;
2007 if( FT_New_Face( p_sys->p_library,
2008 psz_fontfile, i_idx, &p_face ) )
2010 free( psz_fontfile );
2011 free( pp_char_styles );
2012 #if defined(HAVE_FRIBIDI)
2015 free( pi_karaoke_bar );
2016 return VLC_EGENERIC;
2018 free( psz_fontfile );
2022 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2024 /* We've loaded a font face which is unhelpful for actually
2025 * rendering text - fallback to the default one.
2027 FT_Done_Face( p_face );
2031 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2032 ft_encoding_unicode ) ||
2033 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2034 p_style->i_font_size ) )
2036 if( p_face ) FT_Done_Face( p_face );
2037 free( pp_char_styles );
2038 #if defined(HAVE_FRIBIDI)
2041 free( pi_karaoke_bar );
2042 return VLC_EGENERIC;
2044 p_sys->i_use_kerning =
2045 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2048 uint32_t *psz_unicode = (uint32_t *)
2049 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2052 if( p_face ) FT_Done_Face( p_face );
2053 free( pp_char_styles );
2054 free( psz_unicode );
2055 #if defined(HAVE_FRIBIDI)
2058 free( pi_karaoke_bar );
2061 memcpy( psz_unicode, psz_text + i_prev,
2062 sizeof( uint32_t ) * ( k - i_prev ) );
2063 psz_unicode[ k - i_prev ] = 0;
2064 while( *psz_unicode )
2068 if( !(p_line = NewLine( i_len - i_prev)) )
2070 if( p_face ) FT_Done_Face( p_face );
2071 free( pp_char_styles );
2072 free( psz_unicode );
2073 #if defined(HAVE_FRIBIDI)
2076 free( pi_karaoke_bar );
2079 /* New Color mode only works in YUVA rendering mode --
2080 * (RGB mode has palette constraints on it). We therefore
2081 * need to populate the legacy colour fields also.
2083 p_line->b_new_color_mode = true;
2084 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2085 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2086 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2087 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2088 p_line->p_next = NULL;
2090 i_pen_y += tmp_result.y;
2094 if( p_prev ) p_prev->p_next = p_line;
2095 else *pp_lines = p_line;
2098 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2099 p_style->i_font_color, p_style->b_underline,
2100 p_style->i_karaoke_bg_color,
2101 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2102 &tmp_result ) != VLC_SUCCESS )
2104 if( p_face ) FT_Done_Face( p_face );
2105 free( pp_char_styles );
2106 free( psz_unicode );
2107 #if defined(HAVE_FRIBIDI)
2110 free( pi_karaoke_bar );
2111 return VLC_EGENERIC;
2116 p_result->x = __MAX( p_result->x, tmp_result.x );
2117 p_result->y += tmp_result.y;
2122 if( *psz_unicode == '\n')
2126 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2128 *c_ptr = *(c_ptr+1);
2133 free( psz_unicode );
2134 if( p_face ) FT_Done_Face( p_face );
2138 free( pp_char_styles );
2139 #if defined(HAVE_FRIBIDI)
2144 p_result->x = __MAX( p_result->x, tmp_result.x );
2145 p_result->y += tmp_result.y;
2148 if( pi_karaoke_bar )
2151 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2153 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2155 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2159 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2161 /* 100% BG colour will render faster if we
2162 * instead make it 100% FG colour, so leave
2163 * the ratio alone and copy the value across
2165 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2169 if( pi_karaoke_bar[ i ] & 0x80 )
2171 /* Swap Left and Right sides over for Right aligned
2172 * language text (eg. Arabic, Hebrew)
2174 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2176 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2177 p_line->p_bg_rgb[ k ] = i_tmp;
2179 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2182 /* Jump over the '\n' at the line-end */
2185 free( pi_karaoke_bar );
2191 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2192 subpicture_region_t *p_region_in )
2194 int rv = VLC_SUCCESS;
2195 stream_t *p_sub = NULL;
2196 xml_reader_t *p_xml_reader = NULL;
2198 if( !p_region_in || !p_region_in->psz_html )
2199 return VLC_EGENERIC;
2201 /* Reset the default fontsize in case screen metrics have changed */
2202 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2204 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2205 (uint8_t *) p_region_in->psz_html,
2206 strlen( p_region_in->psz_html ),
2210 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2211 if( p_filter->p_sys->p_xml )
2213 bool b_karaoke = false;
2215 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2218 /* Look for Root Node */
2219 if( xml_ReaderRead( p_xml_reader ) == 1 )
2221 char *psz_node = xml_ReaderName( p_xml_reader );
2223 if( !strcasecmp( "karaoke", psz_node ) )
2225 /* We're going to have to render the text a number
2226 * of times to show the progress marker on the text.
2228 var_SetBool( p_filter, "text-rerender", true );
2231 else if( !strcasecmp( "text", psz_node ) )
2237 /* Only text and karaoke tags are supported */
2238 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2239 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2240 p_xml_reader = NULL;
2252 uint32_t i_runs = 0;
2253 uint32_t i_k_runs = 0;
2254 uint32_t *pi_run_lengths = NULL;
2255 uint32_t *pi_k_run_lengths = NULL;
2256 uint32_t *pi_k_durations = NULL;
2257 ft_style_t **pp_styles = NULL;
2259 line_desc_t *p_lines = NULL;
2261 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2262 sizeof( uint32_t ) );
2267 rv = ProcessNodes( p_filter, p_xml_reader,
2268 p_region_in->p_style, psz_text, &i_len,
2269 &i_runs, &pi_run_lengths, &pp_styles,
2271 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2274 p_region_out->i_x = p_region_in->i_x;
2275 p_region_out->i_y = p_region_in->i_y;
2277 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2279 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2280 pi_run_lengths, pp_styles, &p_lines, &result,
2281 b_karaoke, i_k_runs, pi_k_run_lengths,
2285 for( k=0; k<i_runs; k++)
2286 DeleteStyle( pp_styles[k] );
2288 free( pi_run_lengths );
2291 /* Don't attempt to render text that couldn't be layed out
2294 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2296 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2298 Render( p_filter, p_region_out, p_lines,
2299 result.x, result.y );
2303 RenderYUVA( p_filter, p_region_out, p_lines,
2304 result.x, result.y );
2308 FreeLines( p_lines );
2310 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2313 stream_Delete( p_sub );
2319 static char* FontConfig_Select( FcConfig* priv, const char* family,
2320 bool b_bold, bool b_italic, int *i_idx )
2323 FcPattern *pat, *p_pat;
2327 pat = FcPatternCreate();
2328 if (!pat) return NULL;
2330 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2331 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2332 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2333 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2335 FcDefaultSubstitute( pat );
2337 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2339 FcPatternDestroy( pat );
2343 p_pat = FcFontMatch( priv, pat, &result );
2344 FcPatternDestroy( pat );
2345 if( !p_pat ) return NULL;
2347 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2348 || ( val_b != FcTrue ) )
2350 FcPatternDestroy( p_pat );
2353 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2358 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2360 FcPatternDestroy( p_pat );
2365 if( strcasecmp((const char*)val_s, family ) != 0 )
2366 msg_Warn( p_filter, "fontconfig: selected font family is not"
2367 "the requested one: '%s' != '%s'\n",
2368 (const char*)val_s, family );
2371 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2373 FcPatternDestroy( p_pat );
2377 FcPatternDestroy( p_pat );
2378 return strdup( (const char*)val_s );
2382 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2383 uint32_t **psz_text_out, uint32_t *pi_runs,
2384 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2385 ft_style_t *p_style )
2387 VLC_UNUSED(p_filter);
2388 VLC_UNUSED(psz_text_in);
2389 VLC_UNUSED(psz_text_out);
2390 VLC_UNUSED(pi_runs);
2391 VLC_UNUSED(ppi_run_lengths);
2392 VLC_UNUSED(ppp_styles);
2393 VLC_UNUSED(p_style);
2396 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2397 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2401 VLC_UNUSED(p_fonts);
2403 VLC_UNUSED(b_italic);
2404 VLC_UNUSED(b_uline);
2409 static void FreeLine( line_desc_t *p_line )
2412 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2414 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2416 free( p_line->pp_glyphs );
2417 free( p_line->p_glyph_pos );
2418 free( p_line->p_fg_rgb );
2419 free( p_line->p_bg_rgb );
2420 free( p_line->p_fg_bg_ratio );
2421 free( p_line->pi_underline_offset );
2422 free( p_line->pi_underline_thickness );
2426 static void FreeLines( line_desc_t *p_lines )
2428 line_desc_t *p_line, *p_next;
2430 if( !p_lines ) return;
2432 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2434 p_next = p_line->p_next;
2439 static line_desc_t *NewLine( int i_count )
2441 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2443 if( !p_line ) return NULL;
2444 p_line->i_height = 0;
2445 p_line->i_width = 0;
2446 p_line->p_next = NULL;
2448 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2449 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2450 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2451 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2452 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2453 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2454 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2455 if( ( p_line->pp_glyphs == NULL ) ||
2456 ( p_line->p_glyph_pos == NULL ) ||
2457 ( p_line->p_fg_rgb == NULL ) ||
2458 ( p_line->p_bg_rgb == NULL ) ||
2459 ( p_line->p_fg_bg_ratio == NULL ) ||
2460 ( p_line->pi_underline_offset == NULL ) ||
2461 ( p_line->pi_underline_thickness == NULL ) )
2463 free( p_line->pi_underline_thickness );
2464 free( p_line->pi_underline_offset );
2465 free( p_line->p_fg_rgb );
2466 free( p_line->p_bg_rgb );
2467 free( p_line->p_fg_bg_ratio );
2468 free( p_line->p_glyph_pos );
2469 free( p_line->pp_glyphs );
2473 p_line->pp_glyphs[0] = NULL;
2474 p_line->b_new_color_mode = false;
2479 static int GetFontSize( filter_t *p_filter )
2481 filter_sys_t *p_sys = p_filter->p_sys;
2485 if( p_sys->i_default_font_size )
2487 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2488 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2490 i_size = p_sys->i_default_font_size;
2494 var_Get( p_filter, "freetype-rel-fontsize", &val );
2497 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2498 p_filter->p_sys->i_display_height =
2499 p_filter->fmt_out.video.i_height;
2504 msg_Warn( p_filter, "invalid fontsize, using 12" );
2505 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2506 i_size = 12 * val.i_int / 1000;
2513 static int SetFontSize( filter_t *p_filter, int i_size )
2515 filter_sys_t *p_sys = p_filter->p_sys;
2519 i_size = GetFontSize( p_filter );
2521 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2524 p_sys->i_font_size = i_size;
2526 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2528 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2529 return VLC_EGENERIC;
2535 static void YUVFromRGB( uint32_t i_argb,
2536 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2538 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2539 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2540 int i_blue = ( i_argb & 0x000000ff );
2542 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2543 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2544 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2545 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2546 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2547 -585 * i_blue + 4096 + 1048576) >> 13, 240);