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>
36 #include <vlc_filter.h>
37 #include <vlc_stream.h>
39 #include <vlc_input.h>
40 #include <vlc_strings.h>
41 #include <vlc_dialog.h>
42 #include <vlc_memory.h>
43 #include <vlc_charset.h>
48 #include <freetype/ftsynth.h>
49 #include FT_FREETYPE_H
51 #define FT_FLOOR(X) ((X & -64) >> 6)
52 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
54 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
59 #define FC_DEFAULT_FONT "Arial Black"
60 #elif defined( WIN32 )
61 #define DEFAULT_FONT "" /* Default font found at run-time */
62 #define FC_DEFAULT_FONT "Arial"
63 #elif defined( HAVE_MAEMO )
64 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
65 #define FC_DEFAULT_FONT "Nokia Sans Bold"
67 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
68 #define FC_DEFAULT_FONT "Serif Bold"
71 #if defined(HAVE_FRIBIDI)
72 #include <fribidi/fribidi.h>
75 #ifdef HAVE_FONTCONFIG
76 #include <fontconfig/fontconfig.h>
78 #define DEFAULT_FONT FC_DEFAULT_FONT
83 /*****************************************************************************
85 *****************************************************************************/
86 static int Create ( vlc_object_t * );
87 static void Destroy( vlc_object_t * );
89 #define FONT_TEXT N_("Font")
91 #ifdef HAVE_FONTCONFIG
92 #define FONT_LONGTEXT N_("Font family for the font you want to use")
94 #define FONT_LONGTEXT N_("Font file for the font you want to use")
97 #define FONTSIZE_TEXT N_("Font size in pixels")
98 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
99 "that will be rendered on the video. " \
100 "If set to something different than 0 this option will override the " \
101 "relative font size." )
102 #define OPACITY_TEXT N_("Opacity")
103 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
104 "text that will be rendered on the video. 0 = transparent, " \
105 "255 = totally opaque. " )
106 #define COLOR_TEXT N_("Text default color")
107 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
108 "the video. This must be an hexadecimal (like HTML colors). The first two "\
109 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
110 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
111 #define FONTSIZER_TEXT N_("Relative font size")
112 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
113 "fonts that will be rendered on the video. If absolute font size is set, "\
114 "relative size will be overridden." )
116 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
117 static const char *const ppsz_sizes_text[] = {
118 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
119 #define YUVP_TEXT N_("Use YUVP renderer")
120 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
121 "This option is only needed if you want to encode into DVB subtitles" )
122 #define EFFECT_TEXT N_("Font Effect")
123 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
124 "text to improve its readability." )
126 #define EFFECT_BACKGROUND 1
127 #define EFFECT_OUTLINE 2
128 #define EFFECT_OUTLINE_FAT 3
130 static int const pi_effects[] = { 1, 2, 3 };
131 static const char *const ppsz_effects_text[] = {
132 N_("Background"),N_("Outline"), N_("Fat Outline") };
133 static const int pi_color_values[] = {
134 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
135 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
136 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
138 static const char *const ppsz_color_descriptions[] = {
139 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
140 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
141 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
144 set_shortname( N_("Text renderer"))
145 set_description( N_("Freetype2 font renderer") )
146 set_category( CAT_VIDEO )
147 set_subcategory( SUBCAT_VIDEO_SUBPIC )
149 add_font( "freetype-font", DEFAULT_FONT, FONT_TEXT, FONT_LONGTEXT,
152 add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
153 FONTSIZE_LONGTEXT, true )
156 /* opacity valid on 0..255, with default 255 = fully opaque */
157 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
158 OPACITY_TEXT, OPACITY_LONGTEXT, true )
161 /* hook to the color values list, with default 0x00ffffff = white */
162 add_integer( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
163 COLOR_LONGTEXT, false )
164 change_integer_list( pi_color_values, ppsz_color_descriptions )
167 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
168 FONTSIZER_LONGTEXT, false )
169 change_integer_list( pi_sizes, ppsz_sizes_text )
172 add_integer( "freetype-effect", 2, EFFECT_TEXT,
173 EFFECT_LONGTEXT, false )
174 change_integer_list( pi_effects, ppsz_effects_text )
177 add_bool( "freetype-yuvp", false, YUVP_TEXT,
178 YUVP_LONGTEXT, true )
179 set_capability( "text renderer", 100 )
180 add_shortcut( "text" )
181 set_callbacks( Create, Destroy )
186 /*****************************************************************************
188 *****************************************************************************/
190 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
191 static int RenderText( filter_t *, subpicture_region_t *,
192 subpicture_region_t * );
193 #ifdef HAVE_FONTCONFIG
194 static int RenderHtml( filter_t *, subpicture_region_t *,
195 subpicture_region_t * );
196 static char *FontConfig_Select( FcConfig *, const char *,
201 static int LoadFontsFromAttachments( filter_t *p_filter );
203 static int GetFontSize( filter_t *p_filter );
204 static int SetFontSize( filter_t *, int );
205 static void YUVFromRGB( uint32_t i_argb,
206 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
208 typedef struct line_desc_t line_desc_t;
211 /** NULL-terminated list of glyphs making the string */
212 FT_BitmapGlyph *pp_glyphs;
213 /** list of relative positions for the glyphs */
214 FT_Vector *p_glyph_pos;
215 /** list of RGB information for styled text
216 * -- if the rendering mode supports it (RenderYUVA) and
217 * b_new_color_mode is set, then it becomes possible to
218 * have multicoloured text within the subtitles. */
221 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
222 bool b_new_color_mode;
223 /** underline information -- only supplied if text should be underlined */
224 int *pi_underline_offset;
225 uint16_t *pi_underline_thickness;
229 int i_red, i_green, i_blue;
234 static line_desc_t *NewLine( int );
239 uint32_t i_font_color; /* ARGB */
240 uint32_t i_karaoke_bg_color; /* ARGB */
248 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
249 static void FreeLines( line_desc_t * );
250 static void FreeLine( line_desc_t * );
252 /*****************************************************************************
253 * filter_sys_t: freetype local data
254 *****************************************************************************
255 * This structure is part of the video output thread descriptor.
256 * It describes the freetype specific properties of an output thread.
257 *****************************************************************************/
260 FT_Library p_library; /* handle to library */
261 FT_Face p_face; /* handle to face object */
263 uint8_t i_font_opacity;
268 int i_default_font_size;
269 int i_display_height;
270 #ifdef HAVE_FONTCONFIG
271 char* psz_fontfamily;
275 input_attachment_t **pp_font_attachments;
276 int i_font_attachments;
280 #define UCHAR uint32_t
281 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
282 #define TR_FONT_STYLE_PTR ft_style_t *
284 #include "text_renderer.h"
286 /*****************************************************************************
287 * Create: allocates osd-text video thread output method
288 *****************************************************************************
289 * This function allocates and initializes a Clone vout method.
290 *****************************************************************************/
291 static int Create( vlc_object_t *p_this )
293 filter_t *p_filter = (filter_t *)p_this;
295 char *psz_fontfile=NULL;
296 char *psz_fontfamily=NULL;
297 int i_error,fontindex;
299 #ifdef HAVE_FONTCONFIG
300 FcPattern *fontpattern = NULL, *fontmatch = NULL;
301 /* Initialise result to Match, as fontconfig doesnt
302 * really set this other than some error-cases */
303 FcResult fontresult = FcResultMatch;
307 /* Allocate structure */
308 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
311 #ifdef HAVE_FONTCONFIG
312 p_sys->psz_fontfamily = NULL;
316 p_sys->p_library = 0;
317 p_sys->i_font_size = 0;
318 p_sys->i_display_height = 0;
320 var_Create( p_filter, "freetype-rel-fontsize",
321 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
323 psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
324 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
325 p_sys->i_effect = var_InheritInteger( p_filter, "freetype-effect" );
326 p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
327 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
328 p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
329 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
332 if( !psz_fontfamily || !*psz_fontfamily )
334 free( psz_fontfamily );
335 #ifdef HAVE_FONTCONFIG
336 psz_fontfamily=strdup( DEFAULT_FONT );
338 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
339 if( !psz_fontfamily )
342 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
343 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
345 strcpy( psz_fontfamily, DEFAULT_FONT );
347 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
351 #ifdef HAVE_FONTCONFIG
352 msg_Dbg( p_filter, "Building font databases.");
357 dialog_progress_bar_t *p_dialog = NULL;
358 FcConfig *fcConfig = FcInitLoadConfig();
360 p_dialog = dialog_ProgressCreate( p_filter,
361 _("Building font cache"),
362 _("Please wait while your font cache is rebuilt.\n"
363 "This should take less than a few minutes."), NULL );
366 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
368 FcConfigBuildFonts( fcConfig );
370 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
374 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
375 dialog_ProgressDestroy( p_dialog );
379 /* Lets find some fontfile from freetype-font variable family */
381 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
384 fontpattern = FcPatternCreate();
387 msg_Err( p_filter, "Creating fontpattern failed");
391 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
392 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
393 free( psz_fontsize );
395 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
397 msg_Err( p_filter, "FontSubstitute failed");
400 FcDefaultSubstitute( fontpattern );
402 /* testing fontresult here doesn't do any good really, but maybe it will
403 * in future as fontconfig code doesn't set it in all cases and just
404 * returns NULL or doesn't set to to Match on all Match cases.*/
405 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
406 if( !fontmatch || fontresult == FcResultNoMatch )
408 msg_Err( p_filter, "Fontmatching failed");
412 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
413 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
416 msg_Err( p_filter, "Failed to get fontfile");
420 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
421 psz_fontfile ? psz_fontfile : "(null)" );
422 p_sys->psz_fontfamily = strdup( psz_fontfamily );
426 psz_fontfile = psz_fontfamily;
430 i_error = FT_Init_FreeType( &p_sys->p_library );
433 msg_Err( p_filter, "couldn't initialize freetype" );
437 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
438 fontindex, &p_sys->p_face );
440 if( i_error == FT_Err_Unknown_File_Format )
442 msg_Err( p_filter, "file %s have unknown format",
443 psz_fontfile ? psz_fontfile : "(null)" );
448 msg_Err( p_filter, "failed to load font file %s",
449 psz_fontfile ? psz_fontfile : "(null)" );
453 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
456 msg_Err( p_filter, "font has no unicode translation table" );
460 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
462 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
465 p_sys->pp_font_attachments = NULL;
466 p_sys->i_font_attachments = 0;
468 p_filter->pf_render_text = RenderText;
469 #ifdef HAVE_FONTCONFIG
470 p_filter->pf_render_html = RenderHtml;
471 FcPatternDestroy( fontmatch );
472 FcPatternDestroy( fontpattern );
474 p_filter->pf_render_html = NULL;
477 free( psz_fontfamily );
478 LoadFontsFromAttachments( p_filter );
483 #ifdef HAVE_FONTCONFIG
484 if( fontmatch ) FcPatternDestroy( fontmatch );
485 if( fontpattern ) FcPatternDestroy( fontpattern );
489 dialog_ProgressDestroy( p_dialog );
493 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
494 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
495 free( psz_fontfamily );
500 /*****************************************************************************
501 * Destroy: destroy Clone video thread output method
502 *****************************************************************************
503 * Clean up all data and library connections
504 *****************************************************************************/
505 static void Destroy( vlc_object_t *p_this )
507 filter_t *p_filter = (filter_t *)p_this;
508 filter_sys_t *p_sys = p_filter->p_sys;
510 if( p_sys->pp_font_attachments )
514 for( k = 0; k < p_sys->i_font_attachments; k++ )
515 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
517 free( p_sys->pp_font_attachments );
520 #ifdef HAVE_FONTCONFIG
521 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
522 free( p_sys->psz_fontfamily );
525 /* FcFini asserts calling the subfunction FcCacheFini()
526 * even if no other library functions have been made since FcInit(),
527 * so don't call it. */
529 FT_Done_Face( p_sys->p_face );
530 FT_Done_FreeType( p_sys->p_library );
534 /*****************************************************************************
535 * Make any TTF/OTF fonts present in the attachments of the media file
536 * and store them for later use by the FreeType Engine
537 *****************************************************************************/
538 static int LoadFontsFromAttachments( filter_t *p_filter )
540 filter_sys_t *p_sys = p_filter->p_sys;
541 input_attachment_t **pp_attachments;
542 int i_attachments_cnt;
544 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
547 p_sys->i_font_attachments = 0;
548 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
549 if( !p_sys->pp_font_attachments )
552 for( int k = 0; k < i_attachments_cnt; k++ )
554 input_attachment_t *p_attach = pp_attachments[k];
556 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
557 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
558 p_attach->i_data > 0 && p_attach->p_data )
560 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
564 vlc_input_attachment_Delete( p_attach );
567 free( pp_attachments );
572 /*****************************************************************************
573 * Render: place string in picture
574 *****************************************************************************
575 * This function merges the previously rendered freetype glyphs into a picture
576 *****************************************************************************/
577 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
578 line_desc_t *p_line, int i_width, int i_height )
580 VLC_UNUSED(p_filter);
581 static const uint8_t pi_gamma[16] =
582 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
583 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
587 int i, x, y, i_pitch;
588 uint8_t i_y; /* YUV values, derived from incoming RGB */
591 /* Create a new subpicture region */
592 memset( &fmt, 0, sizeof(video_format_t) );
593 fmt.i_chroma = VLC_CODEC_YUVP;
594 fmt.i_width = fmt.i_visible_width = i_width + 4;
595 fmt.i_height = fmt.i_visible_height = i_height + 4;
596 if( p_region->fmt.i_visible_width > 0 )
597 fmt.i_visible_width = p_region->fmt.i_visible_width;
598 if( p_region->fmt.i_visible_height > 0 )
599 fmt.i_visible_height = p_region->fmt.i_visible_height;
600 fmt.i_x_offset = fmt.i_y_offset = 0;
602 assert( !p_region->p_picture );
603 p_region->p_picture = picture_NewFromFormat( &fmt );
604 if( !p_region->p_picture )
606 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
609 /* Calculate text color components */
610 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
611 25 * p_line->i_blue + 128) >> 8) + 16;
612 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
613 112 * p_line->i_blue + 128) >> 8) + 128;
614 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
615 18 * p_line->i_blue + 128) >> 8) + 128;
618 fmt.p_palette->i_entries = 16;
619 for( i = 0; i < 8; i++ )
621 fmt.p_palette->palette[i][0] = 0;
622 fmt.p_palette->palette[i][1] = 0x80;
623 fmt.p_palette->palette[i][2] = 0x80;
624 fmt.p_palette->palette[i][3] = pi_gamma[i];
625 fmt.p_palette->palette[i][3] =
626 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
628 for( i = 8; i < fmt.p_palette->i_entries; i++ )
630 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
631 fmt.p_palette->palette[i][1] = i_u;
632 fmt.p_palette->palette[i][2] = i_v;
633 fmt.p_palette->palette[i][3] = pi_gamma[i];
634 fmt.p_palette->palette[i][3] =
635 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
638 p_dst = p_region->p_picture->Y_PIXELS;
639 i_pitch = p_region->p_picture->Y_PITCH;
641 /* Initialize the region pixels */
642 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
644 for( ; p_line != NULL; p_line = p_line->p_next )
646 int i_glyph_tmax = 0;
647 int i_bitmap_offset, i_offset, i_align_offset = 0;
648 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
650 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
651 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
654 if( p_line->i_width < i_width )
656 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
658 i_align_offset = i_width - p_line->i_width;
660 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
662 i_align_offset = ( i_width - p_line->i_width ) / 2;
666 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
668 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
670 i_offset = ( p_line->p_glyph_pos[ i ].y +
671 i_glyph_tmax - p_glyph->top + 2 ) *
672 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
675 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
677 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
679 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
681 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
688 /* Outlining (find something better than nearest neighbour filtering ?) */
691 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
692 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
693 uint8_t left, current;
695 for( y = 1; y < (int)fmt.i_height - 1; y++ )
697 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
698 p_dst += p_region->p_picture->Y_PITCH;
701 for( x = 1; x < (int)fmt.i_width - 1; x++ )
704 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
705 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;
709 memset( p_top, 0, fmt.i_width );
715 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
716 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
717 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
718 int i_glyph_tmax, int i_align_offset,
719 uint8_t i_y, uint8_t i_u, uint8_t i_v,
720 subpicture_region_t *p_region)
724 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
726 p_dst_y = p_region->p_picture->Y_PIXELS;
727 p_dst_u = p_region->p_picture->U_PIXELS;
728 p_dst_v = p_region->p_picture->V_PIXELS;
729 p_dst_a = p_region->p_picture->A_PIXELS;
730 i_pitch = p_region->p_picture->A_PITCH;
732 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
733 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
735 for( y = 0; y < i_line_thickness; y++ )
737 int i_extra = p_this_glyph->bitmap.width;
741 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
742 (p_this_glyph_pos->x + p_this_glyph->left);
744 for( x = 0; x < i_extra; x++ )
748 /* break the underline around the tails of any glyphs which cross it */
749 /* Strikethrough doesn't get broken */
750 for( z = x - i_line_thickness;
751 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
754 if( p_next_glyph && ( z >= i_extra ) )
756 int i_row = i_line_offset + p_next_glyph->top + y;
758 if( ( p_next_glyph->bitmap.rows > i_row ) &&
759 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
764 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
766 int i_row = i_line_offset + p_this_glyph->top + y;
768 if( ( p_this_glyph->bitmap.rows > i_row ) &&
769 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
778 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
779 p_dst_u[i_offset+x] = i_u;
780 p_dst_v[i_offset+x] = i_v;
781 p_dst_a[i_offset+x] = 255;
788 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
790 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
791 int i_pitch = p_region->p_picture->A_PITCH;
794 for( ; p_line != NULL; p_line = p_line->p_next )
796 int i_glyph_tmax=0, i = 0;
797 int i_bitmap_offset, i_offset, i_align_offset = 0;
798 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
800 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
801 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
804 if( p_line->i_width < i_width )
806 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
808 i_align_offset = i_width - p_line->i_width;
810 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
812 i_align_offset = ( i_width - p_line->i_width ) / 2;
816 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
818 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
820 i_offset = ( p_line->p_glyph_pos[ i ].y +
821 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
822 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
823 i_align_offset +xoffset;
825 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
827 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
829 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
830 if( p_dst[i_offset+x] <
831 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
833 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
842 /*****************************************************************************
843 * Render: place string in picture
844 *****************************************************************************
845 * This function merges the previously rendered freetype glyphs into a picture
846 *****************************************************************************/
847 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
848 line_desc_t *p_line, int i_width, int i_height )
850 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
852 int i, x, y, i_pitch, i_alpha;
853 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
855 if( i_width == 0 || i_height == 0 )
858 /* Create a new subpicture region */
859 memset( &fmt, 0, sizeof(video_format_t) );
860 fmt.i_chroma = VLC_CODEC_YUVA;
861 fmt.i_width = fmt.i_visible_width = i_width + 6;
862 fmt.i_height = fmt.i_visible_height = i_height + 6;
863 if( p_region->fmt.i_visible_width > 0 )
864 fmt.i_visible_width = p_region->fmt.i_visible_width;
865 if( p_region->fmt.i_visible_height > 0 )
866 fmt.i_visible_height = p_region->fmt.i_visible_height;
867 fmt.i_x_offset = fmt.i_y_offset = 0;
869 p_region->p_picture = picture_NewFromFormat( &fmt );
870 if( !p_region->p_picture )
874 /* Calculate text color components */
875 YUVFromRGB( (p_line->i_red << 16) |
876 (p_line->i_green << 8) |
879 i_alpha = p_line->i_alpha;
881 p_dst_y = p_region->p_picture->Y_PIXELS;
882 p_dst_u = p_region->p_picture->U_PIXELS;
883 p_dst_v = p_region->p_picture->V_PIXELS;
884 p_dst_a = p_region->p_picture->A_PIXELS;
885 i_pitch = p_region->p_picture->A_PITCH;
887 /* Initialize the region pixels */
888 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
890 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
891 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
892 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
893 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
897 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
898 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
899 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
900 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
902 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
903 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
905 DrawBlack( p_line, i_width, p_region, 0, 0);
906 DrawBlack( p_line, i_width, p_region, -1, 0);
907 DrawBlack( p_line, i_width, p_region, 0, -1);
908 DrawBlack( p_line, i_width, p_region, 1, 0);
909 DrawBlack( p_line, i_width, p_region, 0, 1);
912 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
914 DrawBlack( p_line, i_width, p_region, -1, -1);
915 DrawBlack( p_line, i_width, p_region, -1, 1);
916 DrawBlack( p_line, i_width, p_region, 1, -1);
917 DrawBlack( p_line, i_width, p_region, 1, 1);
919 DrawBlack( p_line, i_width, p_region, -2, 0);
920 DrawBlack( p_line, i_width, p_region, 0, -2);
921 DrawBlack( p_line, i_width, p_region, 2, 0);
922 DrawBlack( p_line, i_width, p_region, 0, 2);
924 DrawBlack( p_line, i_width, p_region, -2, -2);
925 DrawBlack( p_line, i_width, p_region, -2, 2);
926 DrawBlack( p_line, i_width, p_region, 2, -2);
927 DrawBlack( p_line, i_width, p_region, 2, 2);
929 DrawBlack( p_line, i_width, p_region, -3, 0);
930 DrawBlack( p_line, i_width, p_region, 0, -3);
931 DrawBlack( p_line, i_width, p_region, 3, 0);
932 DrawBlack( p_line, i_width, p_region, 0, 3);
935 for( ; p_line != NULL; p_line = p_line->p_next )
937 int i_glyph_tmax = 0;
938 int i_bitmap_offset, i_offset, i_align_offset = 0;
939 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
941 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
942 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
945 if( p_line->i_width < i_width )
947 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
949 i_align_offset = i_width - p_line->i_width;
951 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
953 i_align_offset = ( i_width - p_line->i_width ) / 2;
957 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
959 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
961 i_offset = ( p_line->p_glyph_pos[ i ].y +
962 i_glyph_tmax - p_glyph->top + 3 ) *
963 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
966 if( p_line->b_new_color_mode )
968 /* Every glyph can (and in fact must) have its own color */
969 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
972 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
974 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
976 uint8_t i_y_local = i_y;
977 uint8_t i_u_local = i_u;
978 uint8_t i_v_local = i_v;
980 if( p_line->p_fg_bg_ratio != 0x00 )
982 int i_split = p_glyph->bitmap.width *
983 p_line->p_fg_bg_ratio[ i ] / 0x7f;
987 YUVFromRGB( p_line->p_bg_rgb[ i ],
988 &i_y_local, &i_u_local, &i_v_local );
992 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
994 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
995 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
997 p_dst_u[i_offset+x] = i_u;
998 p_dst_v[i_offset+x] = i_v;
1000 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1001 p_dst_a[i_offset+x] = 0xff;
1004 i_offset += i_pitch;
1007 if( p_line->pi_underline_thickness[ i ] )
1009 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1010 p_line->pi_underline_offset[ i ],
1011 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1012 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1013 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1014 i_glyph_tmax, i_align_offset,
1021 /* Apply the alpha setting */
1022 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1023 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1029 * This function renders a text subpicture region into another one.
1030 * It also calculates the size needed for this string, and renders the
1031 * needed glyphs into memory. It is used as pf_add_string callback in
1032 * the vout method by this module
1034 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1035 subpicture_region_t *p_region_in )
1037 filter_sys_t *p_sys = p_filter->p_sys;
1038 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1039 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1040 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1041 size_t i_string_length;
1043 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1053 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1054 psz_string = p_region_in->psz_text;
1055 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1057 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1058 i_scale = val.i_int;
1060 if( p_region_in->p_style )
1062 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1063 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1064 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1068 i_font_color = p_sys->i_font_color;
1069 i_font_alpha = 255 - p_sys->i_font_opacity;
1070 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1073 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1074 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1075 SetFontSize( p_filter, i_font_size );
1077 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1078 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1079 i_blue = i_font_color & 0x000000FF;
1081 result.x = result.y = 0;
1082 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1084 #if defined(WORDS_BIGENDIAN)
1085 psz_unicode = ToCharset( "UCS-4BE", psz_string, &i_string_length );
1087 psz_unicode = ToCharset( "UCS-4LE", psz_string, &i_string_length );
1089 if( psz_unicode == NULL )
1091 psz_unicode_orig = psz_unicode;
1092 i_string_length /= 4;
1094 #if defined(HAVE_FRIBIDI)
1096 uint32_t *p_fribidi_string;
1097 int32_t start_pos, pos = 0;
1099 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1100 if( !p_fribidi_string )
1103 /* Do bidi conversion line-by-line */
1104 while( pos < i_string_length )
1106 while( pos < i_string_length )
1108 i_char = psz_unicode[pos];
1109 if (i_char != '\r' && i_char != '\n')
1111 p_fribidi_string[pos] = i_char;
1115 while( pos < i_string_length )
1117 i_char = psz_unicode[pos];
1118 if (i_char == '\r' || i_char == '\n')
1122 if (pos > start_pos)
1124 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1125 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1128 (FriBidiChar*)p_fribidi_string + start_pos,
1133 free( psz_unicode_orig );
1134 psz_unicode = psz_unicode_orig = p_fribidi_string;
1135 p_fribidi_string[ i_string_length ] = 0;
1139 /* Calculate relative glyph positions and a bounding box for the
1141 if( !(p_line = NewLine( strlen( psz_string ))) )
1144 i_pen_x = i_pen_y = 0;
1146 psz_line_start = psz_unicode;
1148 #define face p_sys->p_face
1149 #define glyph face->glyph
1151 while( *psz_unicode )
1153 i_char = *psz_unicode++;
1154 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1159 if( i_char == '\n' )
1161 psz_line_start = psz_unicode;
1162 if( !(p_next = NewLine( strlen( psz_string ))) )
1164 p_line->p_next = p_next;
1165 p_line->i_width = line.xMax;
1166 p_line->i_height = face->size->metrics.height >> 6;
1167 p_line->pp_glyphs[ i ] = NULL;
1168 p_line->i_alpha = i_font_alpha;
1169 p_line->i_red = i_red;
1170 p_line->i_green = i_green;
1171 p_line->i_blue = i_blue;
1174 result.x = __MAX( result.x, line.xMax );
1175 result.y += face->size->metrics.height >> 6;
1178 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1179 i_pen_y += face->size->metrics.height >> 6;
1181 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1186 i_glyph_index = FT_Get_Char_Index( face, i_char );
1187 if( p_sys->i_use_kerning && i_glyph_index
1191 FT_Get_Kerning( face, i_previous, i_glyph_index,
1192 ft_kerning_default, &delta );
1193 i_pen_x += delta.x >> 6;
1196 p_line->p_glyph_pos[ i ].x = i_pen_x;
1197 p_line->p_glyph_pos[ i ].y = i_pen_y;
1198 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1201 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1204 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1209 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1212 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1216 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1217 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1220 FT_Done_Glyph( tmp_glyph );
1223 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1226 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1227 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1228 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1230 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1231 p_line->pp_glyphs[ i ] = NULL;
1233 p_line = NewLine( strlen( psz_string ));
1234 if( p_prev ) p_prev->p_next = p_line;
1235 else p_lines = p_line;
1237 uint32_t *psz_unicode_saved = psz_unicode;
1238 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1242 if( psz_unicode == psz_line_start )
1243 { /* try harder to break that line */
1244 psz_unicode = psz_unicode_saved;
1245 while( psz_unicode > psz_line_start &&
1246 *psz_unicode != '_' && *psz_unicode != '/' &&
1247 *psz_unicode != '\\' && *psz_unicode != '.' )
1252 if( psz_unicode == psz_line_start )
1254 msg_Warn( p_filter, "unbreakable string" );
1259 *psz_unicode = '\n';
1261 psz_unicode = psz_line_start;
1264 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1267 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1268 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1270 i_previous = i_glyph_index;
1271 i_pen_x += glyph->advance.x >> 6;
1275 p_line->i_width = line.xMax;
1276 p_line->i_height = face->size->metrics.height >> 6;
1277 p_line->pp_glyphs[ i ] = NULL;
1278 p_line->i_alpha = i_font_alpha;
1279 p_line->i_red = i_red;
1280 p_line->i_green = i_green;
1281 p_line->i_blue = i_blue;
1282 result.x = __MAX( result.x, line.xMax );
1283 result.y += line.yMax - line.yMin;
1288 p_region_out->i_x = p_region_in->i_x;
1289 p_region_out->i_y = p_region_in->i_y;
1291 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1292 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1294 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1296 free( psz_unicode_orig );
1297 FreeLines( p_lines );
1301 free( psz_unicode_orig );
1302 FreeLines( p_lines );
1303 return VLC_EGENERIC;
1306 #ifdef HAVE_FONTCONFIG
1307 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1308 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1309 bool b_italic, bool b_uline, bool b_through )
1311 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1315 p_style->i_font_size = i_font_size;
1316 p_style->i_font_color = i_font_color;
1317 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1318 p_style->b_italic = b_italic;
1319 p_style->b_bold = b_bold;
1320 p_style->b_underline = b_uline;
1321 p_style->b_through = b_through;
1323 p_style->psz_fontname = strdup( psz_fontname );
1328 static void DeleteStyle( ft_style_t *p_style )
1332 free( p_style->psz_fontname );
1337 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1344 if(( s1->i_font_size == s2->i_font_size ) &&
1345 ( s1->i_font_color == s2->i_font_color ) &&
1346 ( s1->b_italic == s2->b_italic ) &&
1347 ( s1->b_through == s2->b_through ) &&
1348 ( s1->b_bold == s2->b_bold ) &&
1349 ( s1->b_underline == s2->b_underline ) &&
1350 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1357 static void IconvText( filter_t *p_filter, const char *psz_string,
1358 size_t *i_string_length, uint32_t **ppsz_unicode )
1360 *i_string_length = 0;
1361 if( *ppsz_unicode == NULL )
1366 #if defined(WORDS_BIGENDIAN)
1367 ToCharset( "UCS-4BE", psz_string, &i_length );
1369 ToCharset( "UCS-4LE", psz_string, &i_length );
1373 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1376 memcpy( *ppsz_unicode, psz_tmp, i_length );
1377 *i_string_length = i_length / 4;
1382 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1383 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1384 bool b_uline, bool b_through )
1386 ft_style_t *p_style = NULL;
1388 char *psz_fontname = NULL;
1389 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1390 uint32_t i_karaoke_bg_color = i_font_color;
1391 int i_font_size = p_sys->i_font_size;
1393 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1394 &i_font_color, &i_karaoke_bg_color ))
1396 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1397 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1402 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1403 bool b_uline, bool b_through, bool b_bold,
1404 bool b_italic, int i_karaoke_bgcolor,
1405 line_desc_t *p_line, uint32_t *psz_unicode,
1406 int *pi_pen_x, int i_pen_y, int *pi_start,
1407 FT_Vector *p_result )
1412 bool b_first_on_line = true;
1415 int i_pen_x_start = *pi_pen_x;
1417 uint32_t *psz_unicode_start = psz_unicode;
1419 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1421 /* Account for part of line already in position */
1422 for( i=0; i<*pi_start; i++ )
1426 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1427 ft_glyph_bbox_pixels, &glyph_size );
1429 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1430 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1431 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1432 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1438 b_first_on_line = false;
1440 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1446 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1447 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1451 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1452 ft_kerning_default, &delta );
1453 *pi_pen_x += delta.x >> 6;
1455 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1456 p_line->p_glyph_pos[ i ].y = i_pen_y;
1458 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1461 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1465 "unable to render text FT_Load_Glyph returned %d", i_error );
1466 p_line->pp_glyphs[ i ] = NULL;
1467 return VLC_EGENERIC;
1471 /* Do synthetic styling now that Freetype supports it;
1472 * ie. if the font we have loaded is NOT already in the
1473 * style that the tags want, then switch it on; if they
1474 * are then don't. */
1475 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1476 FT_GlyphSlot_Embolden( p_face->glyph );
1477 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1478 FT_GlyphSlot_Oblique( p_face->glyph );
1480 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1484 "unable to render text FT_Get_Glyph returned %d", i_error );
1485 p_line->pp_glyphs[ i ] = NULL;
1486 return VLC_EGENERIC;
1488 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1489 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1492 FT_Done_Glyph( tmp_glyph );
1495 if( b_uline || b_through )
1497 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1498 p_face->size->metrics.y_scale));
1499 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1500 p_face->size->metrics.y_scale));
1502 p_line->pi_underline_offset[ i ] =
1503 ( aOffset < 0 ) ? -aOffset : aOffset;
1504 p_line->pi_underline_thickness[ i ] =
1505 ( aSize < 0 ) ? -aSize : aSize;
1508 /* Move the baseline to make it strikethrough instead of
1509 * underline. That means that strikethrough takes precedence
1511 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1512 p_face->size->metrics.y_scale));
1514 p_line->pi_underline_offset[ i ] -=
1515 ( aDescent < 0 ) ? -aDescent : aDescent;
1519 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1520 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1521 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1522 p_line->p_fg_bg_ratio[ i ] = 0x00;
1524 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1525 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1526 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1528 for( ; i >= *pi_start; i-- )
1529 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1532 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1536 if( psz_unicode == psz_unicode_start )
1538 if( b_first_on_line )
1540 msg_Warn( p_filter, "unbreakable string" );
1541 p_line->pp_glyphs[ i ] = NULL;
1542 return VLC_EGENERIC;
1544 *pi_pen_x = i_pen_x_start;
1546 p_line->i_width = line.xMax;
1547 p_line->i_height = __MAX( p_line->i_height,
1548 p_face->size->metrics.height >> 6 );
1549 p_line->pp_glyphs[ i ] = NULL;
1551 p_result->x = __MAX( p_result->x, line.xMax );
1552 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1553 i_yMax - i_yMin ) );
1558 *psz_unicode = '\n';
1560 psz_unicode = psz_unicode_start;
1561 *pi_pen_x = i_pen_x_start;
1569 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1570 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1572 i_previous = i_glyph_index;
1573 *pi_pen_x += p_face->glyph->advance.x >> 6;
1576 p_line->i_width = line.xMax;
1577 p_line->i_height = __MAX( p_line->i_height,
1578 p_face->size->metrics.height >> 6 );
1579 p_line->pp_glyphs[ i ] = NULL;
1581 p_result->x = __MAX( p_result->x, line.xMax );
1582 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1583 line.yMax - line.yMin ) );
1587 /* Get rid of any text processed - if necessary repositioning
1588 * at the start of a new line of text
1592 *psz_unicode_start = '\0';
1594 else if( psz_unicode > psz_unicode_start )
1596 for( i=0; psz_unicode[ i ]; i++ )
1597 psz_unicode_start[ i ] = psz_unicode[ i ];
1598 psz_unicode_start[ i ] = '\0';
1604 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1605 uint32_t **psz_text_out, uint32_t *pi_runs,
1606 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1607 ft_style_t *p_style )
1609 size_t i_string_length;
1611 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1612 *psz_text_out += i_string_length;
1614 if( ppp_styles && ppi_run_lengths )
1618 /* XXX this logic looks somewhat broken */
1622 *ppp_styles = realloc_or_free( *ppp_styles,
1623 *pi_runs * sizeof( ft_style_t * ) );
1625 else if( *pi_runs == 1 )
1627 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1630 /* We have just malloc'ed this memory successfully -
1631 * *pi_runs HAS to be within the memory area of *ppp_styles */
1634 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1638 /* XXX more iffy logic */
1640 if( *ppi_run_lengths )
1642 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1643 *pi_runs * sizeof( uint32_t ) );
1645 else if( *pi_runs == 1 )
1647 *ppi_run_lengths = (uint32_t *)
1648 malloc( *pi_runs * sizeof( uint32_t ) );
1651 /* same remarks here */
1652 if( *ppi_run_lengths )
1654 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1657 /* If we couldn't use the p_style argument due to memory allocation
1658 * problems above, release it here.
1660 if( p_style ) DeleteStyle( p_style );
1663 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1667 for( k=0; k < p_sys->i_font_attachments; k++ )
1669 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1671 FT_Face p_face = NULL;
1673 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1681 bool match = !strcasecmp( p_face->family_name,
1682 p_style->psz_fontname );
1684 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1685 match = match && p_style->b_bold;
1687 match = match && !p_style->b_bold;
1689 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1690 match = match && p_style->b_italic;
1692 match = match && !p_style->b_italic;
1700 FT_Done_Face( p_face );
1705 return VLC_EGENERIC;
1708 static int ProcessLines( filter_t *p_filter,
1713 uint32_t *pi_run_lengths,
1714 ft_style_t **pp_styles,
1715 line_desc_t **pp_lines,
1717 FT_Vector *p_result,
1721 uint32_t *pi_k_run_lengths,
1722 uint32_t *pi_k_durations )
1724 filter_sys_t *p_sys = p_filter->p_sys;
1725 ft_style_t **pp_char_styles;
1726 int *p_new_positions = NULL;
1727 int8_t *p_levels = NULL;
1728 uint8_t *pi_karaoke_bar = NULL;
1732 /* Assign each character in the text string its style explicitly, so that
1733 * after the characters have been shuffled around by Fribidi, we can re-apply
1734 * the styles, and to simplify the calculation of runs within a line.
1736 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1737 if( !pp_char_styles )
1742 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1743 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1744 * we just won't be able to display the progress bar; at least we'll
1750 for( j = 0; j < i_runs; j++ )
1751 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1752 pp_char_styles[ i++ ] = pp_styles[ j ];
1754 #if defined(HAVE_FRIBIDI)
1756 ft_style_t **pp_char_styles_new;
1757 int *p_old_positions;
1758 uint32_t *p_fribidi_string;
1759 int start_pos, pos = 0;
1761 pp_char_styles_new = (ft_style_t **)
1762 malloc( i_len * sizeof( ft_style_t * ));
1764 p_fribidi_string = (uint32_t *)
1765 malloc( (i_len + 1) * sizeof(uint32_t) );
1766 p_old_positions = (int *)
1767 malloc( (i_len + 1) * sizeof( int ) );
1768 p_new_positions = (int *)
1769 malloc( (i_len + 1) * sizeof( int ) );
1770 p_levels = (int8_t *)
1771 malloc( (i_len + 1) * sizeof( int8_t ) );
1773 if( ! pp_char_styles_new ||
1774 ! p_fribidi_string ||
1775 ! p_old_positions ||
1776 ! p_new_positions ||
1780 free( p_old_positions );
1781 free( p_new_positions );
1782 free( p_fribidi_string );
1783 free( pp_char_styles_new );
1784 free( pi_karaoke_bar );
1786 free( pp_char_styles );
1790 /* Do bidi conversion line-by-line */
1793 while(pos < i_len) {
1794 if (psz_text[pos] != '\n')
1796 p_fribidi_string[pos] = psz_text[pos];
1797 pp_char_styles_new[pos] = pp_char_styles[pos];
1798 p_new_positions[pos] = pos;
1803 while(pos < i_len) {
1804 if (psz_text[pos] == '\n')
1808 if (pos > start_pos)
1810 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1811 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1812 pos - start_pos, &base_dir,
1813 (FriBidiChar*)p_fribidi_string + start_pos,
1814 p_new_positions + start_pos,
1816 p_levels + start_pos );
1817 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1819 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1820 p_old_positions[ j - start_pos ] ];
1821 p_new_positions[ j ] += start_pos;
1825 free( p_old_positions );
1826 free( pp_char_styles );
1827 pp_char_styles = pp_char_styles_new;
1828 psz_text = p_fribidi_string;
1829 p_fribidi_string[ i_len ] = 0;
1832 /* Work out the karaoke */
1833 if( pi_karaoke_bar )
1835 int64_t i_last_duration = 0;
1836 int64_t i_duration = 0;
1837 int64_t i_start_pos = 0;
1838 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1840 for( k = 0; k< i_k_runs; k++ )
1842 double fraction = 0.0;
1844 i_duration += pi_k_durations[ k ];
1846 if( i_duration < i_elapsed )
1848 /* Completely finished this run-length -
1849 * let it render normally */
1853 else if( i_elapsed < i_last_duration )
1855 /* Haven't got up to this segment yet -
1856 * render it completely in karaoke BG mode */
1862 /* Partway through this run */
1864 fraction = (double)(i_elapsed - i_last_duration) /
1865 (double)pi_k_durations[ k ];
1867 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1869 double shade = pi_k_run_lengths[ k ] * fraction;
1871 if( p_new_positions )
1872 j = p_new_positions[ i_start_pos + i ];
1874 j = i_start_pos + i;
1876 if( i < (uint32_t)shade )
1877 pi_karaoke_bar[ j ] = 0xff;
1878 else if( (double)i > shade )
1879 pi_karaoke_bar[ j ] = 0x00;
1882 shade -= (int)shade;
1883 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1884 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1888 i_last_duration = i_duration;
1889 i_start_pos += pi_k_run_lengths[ k ];
1893 free( p_new_positions );
1895 FT_Vector tmp_result;
1897 line_desc_t *p_line = NULL;
1898 line_desc_t *p_prev = NULL;
1904 p_result->x = p_result->y = 0;
1905 tmp_result.x = tmp_result.y = 0;
1908 for( k = 0; k <= (uint32_t) i_len; k++ )
1910 if( ( k == (uint32_t) i_len ) ||
1912 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1914 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1916 /* End of the current style run */
1917 FT_Face p_face = NULL;
1920 /* Look for a match amongst our attachments first */
1921 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1925 char *psz_fontfile = NULL;
1927 psz_fontfile = FontConfig_Select( NULL,
1928 p_style->psz_fontname,
1932 if( psz_fontfile && ! *psz_fontfile )
1934 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1935 " so using default font", p_style->psz_fontname,
1936 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1937 (p_style->b_bold ? "(Bold)" :
1938 (p_style->b_italic ? "(Italic)" : ""))) );
1939 free( psz_fontfile );
1940 psz_fontfile = NULL;
1945 if( FT_New_Face( p_sys->p_library,
1946 psz_fontfile, i_idx, &p_face ) )
1948 free( psz_fontfile );
1949 free( pp_char_styles );
1950 #if defined(HAVE_FRIBIDI)
1953 free( pi_karaoke_bar );
1954 return VLC_EGENERIC;
1956 free( psz_fontfile );
1960 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1962 /* We've loaded a font face which is unhelpful for actually
1963 * rendering text - fallback to the default one.
1965 FT_Done_Face( p_face );
1969 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1970 ft_encoding_unicode ) ||
1971 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1972 p_style->i_font_size ) )
1974 if( p_face ) FT_Done_Face( p_face );
1975 free( pp_char_styles );
1976 #if defined(HAVE_FRIBIDI)
1979 free( pi_karaoke_bar );
1980 return VLC_EGENERIC;
1982 p_sys->i_use_kerning =
1983 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1986 uint32_t *psz_unicode = (uint32_t *)
1987 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1990 if( p_face ) FT_Done_Face( p_face );
1991 free( pp_char_styles );
1992 free( psz_unicode );
1993 #if defined(HAVE_FRIBIDI)
1996 free( pi_karaoke_bar );
1999 memcpy( psz_unicode, psz_text + i_prev,
2000 sizeof( uint32_t ) * ( k - i_prev ) );
2001 psz_unicode[ k - i_prev ] = 0;
2002 while( *psz_unicode )
2006 if( !(p_line = NewLine( i_len - i_prev)) )
2008 if( p_face ) FT_Done_Face( p_face );
2009 free( pp_char_styles );
2010 free( psz_unicode );
2011 #if defined(HAVE_FRIBIDI)
2014 free( pi_karaoke_bar );
2017 /* New Color mode only works in YUVA rendering mode --
2018 * (RGB mode has palette constraints on it). We therefore
2019 * need to populate the legacy colour fields also.
2021 p_line->b_new_color_mode = true;
2022 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2023 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2024 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2025 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2026 p_line->p_next = NULL;
2028 i_pen_y += tmp_result.y;
2032 if( p_prev ) p_prev->p_next = p_line;
2033 else *pp_lines = p_line;
2036 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2037 p_style->i_font_color, p_style->b_underline,
2041 p_style->i_karaoke_bg_color,
2042 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2043 &tmp_result ) != VLC_SUCCESS )
2045 if( p_face ) FT_Done_Face( p_face );
2046 free( pp_char_styles );
2047 free( psz_unicode );
2048 #if defined(HAVE_FRIBIDI)
2051 free( pi_karaoke_bar );
2052 return VLC_EGENERIC;
2057 p_result->x = __MAX( p_result->x, tmp_result.x );
2058 p_result->y += tmp_result.y;
2063 if( *psz_unicode == '\n')
2067 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2069 *c_ptr = *(c_ptr+1);
2074 free( psz_unicode );
2075 if( p_face ) FT_Done_Face( p_face );
2079 free( pp_char_styles );
2080 #if defined(HAVE_FRIBIDI)
2085 p_result->x = __MAX( p_result->x, tmp_result.x );
2086 p_result->y += tmp_result.y;
2089 if( pi_karaoke_bar )
2092 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2094 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2096 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2100 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2102 /* 100% BG colour will render faster if we
2103 * instead make it 100% FG colour, so leave
2104 * the ratio alone and copy the value across
2106 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2110 if( pi_karaoke_bar[ i ] & 0x80 )
2112 /* Swap Left and Right sides over for Right aligned
2113 * language text (eg. Arabic, Hebrew)
2115 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2117 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2118 p_line->p_bg_rgb[ k ] = i_tmp;
2120 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2123 /* Jump over the '\n' at the line-end */
2126 free( pi_karaoke_bar );
2132 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2133 subpicture_region_t *p_region_in )
2135 int rv = VLC_SUCCESS;
2136 stream_t *p_sub = NULL;
2138 if( !p_region_in || !p_region_in->psz_html )
2139 return VLC_EGENERIC;
2141 /* Reset the default fontsize in case screen metrics have changed */
2142 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2144 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2145 (uint8_t *) p_region_in->psz_html,
2146 strlen( p_region_in->psz_html ),
2148 if( unlikely(p_sub == NULL) )
2151 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2152 bool b_karaoke = false;
2155 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2157 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2159 p_filter->p_sys->p_xml = p_xml_reader;
2162 /* Look for Root Node */
2163 if( xml_ReaderRead( p_xml_reader ) == 1 )
2165 char *psz_node = xml_ReaderName( p_xml_reader );
2167 if( !strcasecmp( "karaoke", psz_node ) )
2169 /* We're going to have to render the text a number
2170 * of times to show the progress marker on the text.
2172 var_SetBool( p_filter, "text-rerender", true );
2175 else if( !strcasecmp( "text", psz_node ) )
2181 /* Only text and karaoke tags are supported */
2182 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2183 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2184 p_xml_reader = NULL;
2196 uint32_t i_runs = 0;
2197 uint32_t i_k_runs = 0;
2198 uint32_t *pi_run_lengths = NULL;
2199 uint32_t *pi_k_run_lengths = NULL;
2200 uint32_t *pi_k_durations = NULL;
2201 ft_style_t **pp_styles = NULL;
2203 line_desc_t *p_lines = NULL;
2205 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2206 sizeof( uint32_t ) );
2209 rv = ProcessNodes( p_filter, p_xml_reader,
2210 p_region_in->p_style, psz_text, &i_len,
2211 &i_runs, &pi_run_lengths, &pp_styles,
2212 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2215 p_region_out->i_x = p_region_in->i_x;
2216 p_region_out->i_y = p_region_in->i_y;
2218 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2220 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2221 pi_run_lengths, pp_styles, &p_lines,
2222 &result, b_karaoke, i_k_runs,
2223 pi_k_run_lengths, pi_k_durations );
2226 for( uint_fast32_t k=0; k<i_runs; k++)
2227 DeleteStyle( pp_styles[k] );
2229 free( pi_run_lengths );
2232 /* Don't attempt to render text that couldn't be layed out
2234 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2236 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2237 Render( p_filter, p_region_out, p_lines,
2238 result.x, result.y );
2240 RenderYUVA( p_filter, p_region_out, p_lines,
2241 result.x, result.y );
2244 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2245 FreeLines( p_lines );
2247 stream_Delete( p_sub );
2251 static char* FontConfig_Select( FcConfig* priv, const char* family,
2252 bool b_bold, bool b_italic, int *i_idx )
2255 FcPattern *pat, *p_pat;
2259 pat = FcPatternCreate();
2260 if (!pat) return NULL;
2262 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2263 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2264 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2265 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2267 FcDefaultSubstitute( pat );
2269 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2271 FcPatternDestroy( pat );
2275 p_pat = FcFontMatch( priv, pat, &result );
2276 FcPatternDestroy( pat );
2277 if( !p_pat ) return NULL;
2279 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2280 || ( val_b != FcTrue ) )
2282 FcPatternDestroy( p_pat );
2285 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2290 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2292 FcPatternDestroy( p_pat );
2297 if( strcasecmp((const char*)val_s, family ) != 0 )
2298 msg_Warn( p_filter, "fontconfig: selected font family is not"
2299 "the requested one: '%s' != '%s'\n",
2300 (const char*)val_s, family );
2303 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2305 FcPatternDestroy( p_pat );
2309 FcPatternDestroy( p_pat );
2310 return strdup( (const char*)val_s );
2314 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2315 uint32_t **psz_text_out, uint32_t *pi_runs,
2316 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2317 ft_style_t *p_style )
2319 VLC_UNUSED(p_filter);
2320 VLC_UNUSED(psz_text_in);
2321 VLC_UNUSED(psz_text_out);
2322 VLC_UNUSED(pi_runs);
2323 VLC_UNUSED(ppi_run_lengths);
2324 VLC_UNUSED(ppp_styles);
2325 VLC_UNUSED(p_style);
2328 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2329 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2330 bool b_uline, bool b_through )
2333 VLC_UNUSED(p_fonts);
2335 VLC_UNUSED(b_italic);
2336 VLC_UNUSED(b_uline);
2337 VLC_UNUSED(b_through);
2342 static void FreeLine( line_desc_t *p_line )
2345 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2347 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2349 free( p_line->pp_glyphs );
2350 free( p_line->p_glyph_pos );
2351 free( p_line->p_fg_rgb );
2352 free( p_line->p_bg_rgb );
2353 free( p_line->p_fg_bg_ratio );
2354 free( p_line->pi_underline_offset );
2355 free( p_line->pi_underline_thickness );
2359 static void FreeLines( line_desc_t *p_lines )
2361 line_desc_t *p_line, *p_next;
2363 if( !p_lines ) return;
2365 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2367 p_next = p_line->p_next;
2372 static line_desc_t *NewLine( int i_count )
2374 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2376 if( !p_line ) return NULL;
2377 p_line->i_height = 0;
2378 p_line->i_width = 0;
2379 p_line->p_next = NULL;
2381 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2382 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2383 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2384 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2385 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2386 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2387 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2388 if( ( p_line->pp_glyphs == NULL ) ||
2389 ( p_line->p_glyph_pos == NULL ) ||
2390 ( p_line->p_fg_rgb == NULL ) ||
2391 ( p_line->p_bg_rgb == NULL ) ||
2392 ( p_line->p_fg_bg_ratio == NULL ) ||
2393 ( p_line->pi_underline_offset == NULL ) ||
2394 ( p_line->pi_underline_thickness == NULL ) )
2396 free( p_line->pi_underline_thickness );
2397 free( p_line->pi_underline_offset );
2398 free( p_line->p_fg_rgb );
2399 free( p_line->p_bg_rgb );
2400 free( p_line->p_fg_bg_ratio );
2401 free( p_line->p_glyph_pos );
2402 free( p_line->pp_glyphs );
2406 p_line->pp_glyphs[0] = NULL;
2407 p_line->b_new_color_mode = false;
2412 static int GetFontSize( filter_t *p_filter )
2414 filter_sys_t *p_sys = p_filter->p_sys;
2418 if( p_sys->i_default_font_size )
2420 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2421 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2423 i_size = p_sys->i_default_font_size;
2427 var_Get( p_filter, "freetype-rel-fontsize", &val );
2430 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2431 p_filter->p_sys->i_display_height =
2432 p_filter->fmt_out.video.i_height;
2437 msg_Warn( p_filter, "invalid fontsize, using 12" );
2438 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2439 i_size = 12 * val.i_int / 1000;
2446 static int SetFontSize( filter_t *p_filter, int i_size )
2448 filter_sys_t *p_sys = p_filter->p_sys;
2452 i_size = GetFontSize( p_filter );
2454 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2457 p_sys->i_font_size = i_size;
2459 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2461 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2462 return VLC_EGENERIC;
2468 static void YUVFromRGB( uint32_t i_argb,
2469 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2471 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2472 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2473 int i_blue = ( i_argb & 0x000000ff );
2475 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2476 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2477 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2478 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2479 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2480 -585 * i_blue + 4096 + 1048576) >> 13, 240);