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 )
155 /* opacity valid on 0..255, with default 255 = fully opaque */
156 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
157 OPACITY_TEXT, OPACITY_LONGTEXT, true )
159 /* hook to the color values list, with default 0x00ffffff = white */
160 add_integer( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
161 COLOR_LONGTEXT, false )
162 change_integer_list( pi_color_values, ppsz_color_descriptions )
164 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
165 FONTSIZER_LONGTEXT, false )
166 change_integer_list( pi_sizes, ppsz_sizes_text )
167 add_integer( "freetype-effect", 2, EFFECT_TEXT,
168 EFFECT_LONGTEXT, false )
169 change_integer_list( pi_effects, ppsz_effects_text )
171 add_bool( "freetype-yuvp", false, YUVP_TEXT,
172 YUVP_LONGTEXT, true )
173 set_capability( "text renderer", 100 )
174 add_shortcut( "text" )
175 set_callbacks( Create, Destroy )
180 /*****************************************************************************
182 *****************************************************************************/
184 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
185 static int RenderText( filter_t *, subpicture_region_t *,
186 subpicture_region_t * );
187 #ifdef HAVE_FONTCONFIG
188 static int RenderHtml( filter_t *, subpicture_region_t *,
189 subpicture_region_t * );
190 static char *FontConfig_Select( FcConfig *, const char *,
195 static int LoadFontsFromAttachments( filter_t *p_filter );
197 static int GetFontSize( filter_t *p_filter );
198 static int SetFontSize( filter_t *, int );
199 static void YUVFromRGB( uint32_t i_argb,
200 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
202 typedef struct line_desc_t line_desc_t;
205 /** NULL-terminated list of glyphs making the string */
206 FT_BitmapGlyph *pp_glyphs;
207 /** list of relative positions for the glyphs */
208 FT_Vector *p_glyph_pos;
209 /** list of RGB information for styled text
210 * -- if the rendering mode supports it (RenderYUVA) and
211 * b_new_color_mode is set, then it becomes possible to
212 * have multicoloured text within the subtitles. */
215 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
216 bool b_new_color_mode;
217 /** underline information -- only supplied if text should be underlined */
218 int *pi_underline_offset;
219 uint16_t *pi_underline_thickness;
223 int i_red, i_green, i_blue;
228 static line_desc_t *NewLine( int );
233 uint32_t i_font_color; /* ARGB */
234 uint32_t i_karaoke_bg_color; /* ARGB */
242 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
243 static void FreeLines( line_desc_t * );
244 static void FreeLine( line_desc_t * );
246 /*****************************************************************************
247 * filter_sys_t: freetype local data
248 *****************************************************************************
249 * This structure is part of the video output thread descriptor.
250 * It describes the freetype specific properties of an output thread.
251 *****************************************************************************/
254 FT_Library p_library; /* handle to library */
255 FT_Face p_face; /* handle to face object */
257 uint8_t i_font_opacity;
262 int i_default_font_size;
263 int i_display_height;
264 #ifdef HAVE_FONTCONFIG
265 char* psz_fontfamily;
269 input_attachment_t **pp_font_attachments;
270 int i_font_attachments;
274 #define UCHAR uint32_t
275 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
276 #define TR_FONT_STYLE_PTR ft_style_t *
278 #include "text_renderer.h"
280 /*****************************************************************************
281 * Create: allocates osd-text video thread output method
282 *****************************************************************************
283 * This function allocates and initializes a Clone vout method.
284 *****************************************************************************/
285 static int Create( vlc_object_t *p_this )
287 filter_t *p_filter = (filter_t *)p_this;
289 char *psz_fontfile=NULL;
290 char *psz_fontfamily=NULL;
291 int i_error,fontindex;
293 #ifdef HAVE_FONTCONFIG
294 FcPattern *fontpattern = NULL, *fontmatch = NULL;
295 /* Initialise result to Match, as fontconfig doesnt
296 * really set this other than some error-cases */
297 FcResult fontresult = FcResultMatch;
301 /* Allocate structure */
302 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
305 #ifdef HAVE_FONTCONFIG
306 p_sys->psz_fontfamily = NULL;
310 p_sys->p_library = 0;
311 p_sys->i_font_size = 0;
312 p_sys->i_display_height = 0;
314 var_Create( p_filter, "freetype-rel-fontsize",
315 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
317 psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
318 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
319 p_sys->i_effect = var_InheritInteger( p_filter, "freetype-effect" );
320 p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
321 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
322 p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
323 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
326 if( !psz_fontfamily || !*psz_fontfamily )
328 free( psz_fontfamily );
329 #ifdef HAVE_FONTCONFIG
330 psz_fontfamily=strdup( DEFAULT_FONT );
332 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
333 if( !psz_fontfamily )
336 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
337 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
339 strcpy( psz_fontfamily, DEFAULT_FONT );
341 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
345 #ifdef HAVE_FONTCONFIG
346 msg_Dbg( p_filter, "Building font databases.");
351 dialog_progress_bar_t *p_dialog = NULL;
352 FcConfig *fcConfig = FcInitLoadConfig();
354 p_dialog = dialog_ProgressCreate( p_filter,
355 _("Building font cache"),
356 _("Please wait while your font cache is rebuilt.\n"
357 "This should take less than a few minutes."), NULL );
360 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
362 FcConfigBuildFonts( fcConfig );
364 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
368 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
369 dialog_ProgressDestroy( p_dialog );
373 /* Lets find some fontfile from freetype-font variable family */
375 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
378 fontpattern = FcPatternCreate();
381 msg_Err( p_filter, "Creating fontpattern failed");
385 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
386 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
387 free( psz_fontsize );
389 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
391 msg_Err( p_filter, "FontSubstitute failed");
394 FcDefaultSubstitute( fontpattern );
396 /* testing fontresult here doesn't do any good really, but maybe it will
397 * in future as fontconfig code doesn't set it in all cases and just
398 * returns NULL or doesn't set to to Match on all Match cases.*/
399 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
400 if( !fontmatch || fontresult == FcResultNoMatch )
402 msg_Err( p_filter, "Fontmatching failed");
406 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
407 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
410 msg_Err( p_filter, "Failed to get fontfile");
414 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
415 psz_fontfile ? psz_fontfile : "(null)" );
416 p_sys->psz_fontfamily = strdup( psz_fontfamily );
420 psz_fontfile = psz_fontfamily;
424 i_error = FT_Init_FreeType( &p_sys->p_library );
427 msg_Err( p_filter, "couldn't initialize freetype" );
431 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
432 fontindex, &p_sys->p_face );
434 if( i_error == FT_Err_Unknown_File_Format )
436 msg_Err( p_filter, "file %s have unknown format",
437 psz_fontfile ? psz_fontfile : "(null)" );
442 msg_Err( p_filter, "failed to load font file %s",
443 psz_fontfile ? psz_fontfile : "(null)" );
447 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
450 msg_Err( p_filter, "font has no unicode translation table" );
454 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
456 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
459 p_sys->pp_font_attachments = NULL;
460 p_sys->i_font_attachments = 0;
462 p_filter->pf_render_text = RenderText;
463 #ifdef HAVE_FONTCONFIG
464 p_filter->pf_render_html = RenderHtml;
465 FcPatternDestroy( fontmatch );
466 FcPatternDestroy( fontpattern );
468 p_filter->pf_render_html = NULL;
471 free( psz_fontfamily );
472 LoadFontsFromAttachments( p_filter );
477 #ifdef HAVE_FONTCONFIG
478 if( fontmatch ) FcPatternDestroy( fontmatch );
479 if( fontpattern ) FcPatternDestroy( fontpattern );
483 dialog_ProgressDestroy( p_dialog );
487 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
488 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
489 free( psz_fontfamily );
494 /*****************************************************************************
495 * Destroy: destroy Clone video thread output method
496 *****************************************************************************
497 * Clean up all data and library connections
498 *****************************************************************************/
499 static void Destroy( vlc_object_t *p_this )
501 filter_t *p_filter = (filter_t *)p_this;
502 filter_sys_t *p_sys = p_filter->p_sys;
504 if( p_sys->pp_font_attachments )
508 for( k = 0; k < p_sys->i_font_attachments; k++ )
509 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
511 free( p_sys->pp_font_attachments );
514 #ifdef HAVE_FONTCONFIG
515 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
516 free( p_sys->psz_fontfamily );
519 /* FcFini asserts calling the subfunction FcCacheFini()
520 * even if no other library functions have been made since FcInit(),
521 * so don't call it. */
523 FT_Done_Face( p_sys->p_face );
524 FT_Done_FreeType( p_sys->p_library );
528 /*****************************************************************************
529 * Make any TTF/OTF fonts present in the attachments of the media file
530 * and store them for later use by the FreeType Engine
531 *****************************************************************************/
532 static int LoadFontsFromAttachments( filter_t *p_filter )
534 filter_sys_t *p_sys = p_filter->p_sys;
535 input_attachment_t **pp_attachments;
536 int i_attachments_cnt;
538 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
541 p_sys->i_font_attachments = 0;
542 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
543 if( !p_sys->pp_font_attachments )
546 for( int k = 0; k < i_attachments_cnt; k++ )
548 input_attachment_t *p_attach = pp_attachments[k];
550 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
551 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
552 p_attach->i_data > 0 && p_attach->p_data )
554 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
558 vlc_input_attachment_Delete( p_attach );
561 free( pp_attachments );
566 /*****************************************************************************
567 * Render: place string in picture
568 *****************************************************************************
569 * This function merges the previously rendered freetype glyphs into a picture
570 *****************************************************************************/
571 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
572 line_desc_t *p_line, int i_width, int i_height )
574 VLC_UNUSED(p_filter);
575 static const uint8_t pi_gamma[16] =
576 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
577 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
581 int i, x, y, i_pitch;
582 uint8_t i_y; /* YUV values, derived from incoming RGB */
585 /* Create a new subpicture region */
586 memset( &fmt, 0, sizeof(video_format_t) );
587 fmt.i_chroma = VLC_CODEC_YUVP;
588 fmt.i_width = fmt.i_visible_width = i_width + 4;
589 fmt.i_height = fmt.i_visible_height = i_height + 4;
590 if( p_region->fmt.i_visible_width > 0 )
591 fmt.i_visible_width = p_region->fmt.i_visible_width;
592 if( p_region->fmt.i_visible_height > 0 )
593 fmt.i_visible_height = p_region->fmt.i_visible_height;
594 fmt.i_x_offset = fmt.i_y_offset = 0;
596 assert( !p_region->p_picture );
597 p_region->p_picture = picture_NewFromFormat( &fmt );
598 if( !p_region->p_picture )
600 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
603 /* Calculate text color components */
604 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
605 25 * p_line->i_blue + 128) >> 8) + 16;
606 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
607 112 * p_line->i_blue + 128) >> 8) + 128;
608 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
609 18 * p_line->i_blue + 128) >> 8) + 128;
612 fmt.p_palette->i_entries = 16;
613 for( i = 0; i < 8; i++ )
615 fmt.p_palette->palette[i][0] = 0;
616 fmt.p_palette->palette[i][1] = 0x80;
617 fmt.p_palette->palette[i][2] = 0x80;
618 fmt.p_palette->palette[i][3] = pi_gamma[i];
619 fmt.p_palette->palette[i][3] =
620 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
622 for( i = 8; i < fmt.p_palette->i_entries; i++ )
624 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
625 fmt.p_palette->palette[i][1] = i_u;
626 fmt.p_palette->palette[i][2] = i_v;
627 fmt.p_palette->palette[i][3] = pi_gamma[i];
628 fmt.p_palette->palette[i][3] =
629 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
632 p_dst = p_region->p_picture->Y_PIXELS;
633 i_pitch = p_region->p_picture->Y_PITCH;
635 /* Initialize the region pixels */
636 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
638 for( ; p_line != NULL; p_line = p_line->p_next )
640 int i_glyph_tmax = 0;
641 int i_bitmap_offset, i_offset, i_align_offset = 0;
642 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
644 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
645 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
648 if( p_line->i_width < i_width )
650 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
652 i_align_offset = i_width - p_line->i_width;
654 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
656 i_align_offset = ( i_width - p_line->i_width ) / 2;
660 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
662 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
664 i_offset = ( p_line->p_glyph_pos[ i ].y +
665 i_glyph_tmax - p_glyph->top + 2 ) *
666 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
669 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
671 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
673 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
675 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
682 /* Outlining (find something better than nearest neighbour filtering ?) */
685 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
686 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
687 uint8_t left, current;
689 for( y = 1; y < (int)fmt.i_height - 1; y++ )
691 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
692 p_dst += p_region->p_picture->Y_PITCH;
695 for( x = 1; x < (int)fmt.i_width - 1; x++ )
698 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
699 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;
703 memset( p_top, 0, fmt.i_width );
709 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
710 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
711 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
712 int i_glyph_tmax, int i_align_offset,
713 uint8_t i_y, uint8_t i_u, uint8_t i_v,
714 subpicture_region_t *p_region)
718 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
720 p_dst_y = p_region->p_picture->Y_PIXELS;
721 p_dst_u = p_region->p_picture->U_PIXELS;
722 p_dst_v = p_region->p_picture->V_PIXELS;
723 p_dst_a = p_region->p_picture->A_PIXELS;
724 i_pitch = p_region->p_picture->A_PITCH;
726 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
727 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
729 for( y = 0; y < i_line_thickness; y++ )
731 int i_extra = p_this_glyph->bitmap.width;
735 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
736 (p_this_glyph_pos->x + p_this_glyph->left);
738 for( x = 0; x < i_extra; x++ )
742 /* break the underline around the tails of any glyphs which cross it */
743 /* Strikethrough doesn't get broken */
744 for( z = x - i_line_thickness;
745 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
748 if( p_next_glyph && ( z >= i_extra ) )
750 int i_row = i_line_offset + p_next_glyph->top + y;
752 if( ( p_next_glyph->bitmap.rows > i_row ) &&
753 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
758 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
760 int i_row = i_line_offset + p_this_glyph->top + y;
762 if( ( p_this_glyph->bitmap.rows > i_row ) &&
763 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
772 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
773 p_dst_u[i_offset+x] = i_u;
774 p_dst_v[i_offset+x] = i_v;
775 p_dst_a[i_offset+x] = 255;
782 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
784 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
785 int i_pitch = p_region->p_picture->A_PITCH;
788 for( ; p_line != NULL; p_line = p_line->p_next )
790 int i_glyph_tmax=0, i = 0;
791 int i_bitmap_offset, i_offset, i_align_offset = 0;
792 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
794 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
795 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
798 if( p_line->i_width < i_width )
800 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
802 i_align_offset = i_width - p_line->i_width;
804 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
806 i_align_offset = ( i_width - p_line->i_width ) / 2;
810 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
812 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
814 i_offset = ( p_line->p_glyph_pos[ i ].y +
815 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
816 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
817 i_align_offset +xoffset;
819 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
821 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
823 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
824 if( p_dst[i_offset+x] <
825 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
827 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
836 /*****************************************************************************
837 * Render: place string in picture
838 *****************************************************************************
839 * This function merges the previously rendered freetype glyphs into a picture
840 *****************************************************************************/
841 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
842 line_desc_t *p_line, int i_width, int i_height )
844 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
846 int i, x, y, i_pitch, i_alpha;
847 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
849 if( i_width == 0 || i_height == 0 )
852 /* Create a new subpicture region */
853 memset( &fmt, 0, sizeof(video_format_t) );
854 fmt.i_chroma = VLC_CODEC_YUVA;
855 fmt.i_width = fmt.i_visible_width = i_width + 6;
856 fmt.i_height = fmt.i_visible_height = i_height + 6;
857 if( p_region->fmt.i_visible_width > 0 )
858 fmt.i_visible_width = p_region->fmt.i_visible_width;
859 if( p_region->fmt.i_visible_height > 0 )
860 fmt.i_visible_height = p_region->fmt.i_visible_height;
861 fmt.i_x_offset = fmt.i_y_offset = 0;
863 p_region->p_picture = picture_NewFromFormat( &fmt );
864 if( !p_region->p_picture )
868 /* Calculate text color components */
869 YUVFromRGB( (p_line->i_red << 16) |
870 (p_line->i_green << 8) |
873 i_alpha = p_line->i_alpha;
875 p_dst_y = p_region->p_picture->Y_PIXELS;
876 p_dst_u = p_region->p_picture->U_PIXELS;
877 p_dst_v = p_region->p_picture->V_PIXELS;
878 p_dst_a = p_region->p_picture->A_PIXELS;
879 i_pitch = p_region->p_picture->A_PITCH;
881 /* Initialize the region pixels */
882 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
884 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
885 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
886 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
887 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
891 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
892 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
893 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
894 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
896 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
897 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
899 DrawBlack( p_line, i_width, p_region, 0, 0);
900 DrawBlack( p_line, i_width, p_region, -1, 0);
901 DrawBlack( p_line, i_width, p_region, 0, -1);
902 DrawBlack( p_line, i_width, p_region, 1, 0);
903 DrawBlack( p_line, i_width, p_region, 0, 1);
906 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
908 DrawBlack( p_line, i_width, p_region, -1, -1);
909 DrawBlack( p_line, i_width, p_region, -1, 1);
910 DrawBlack( p_line, i_width, p_region, 1, -1);
911 DrawBlack( p_line, i_width, p_region, 1, 1);
913 DrawBlack( p_line, i_width, p_region, -2, 0);
914 DrawBlack( p_line, i_width, p_region, 0, -2);
915 DrawBlack( p_line, i_width, p_region, 2, 0);
916 DrawBlack( p_line, i_width, p_region, 0, 2);
918 DrawBlack( p_line, i_width, p_region, -2, -2);
919 DrawBlack( p_line, i_width, p_region, -2, 2);
920 DrawBlack( p_line, i_width, p_region, 2, -2);
921 DrawBlack( p_line, i_width, p_region, 2, 2);
923 DrawBlack( p_line, i_width, p_region, -3, 0);
924 DrawBlack( p_line, i_width, p_region, 0, -3);
925 DrawBlack( p_line, i_width, p_region, 3, 0);
926 DrawBlack( p_line, i_width, p_region, 0, 3);
929 for( ; p_line != NULL; p_line = p_line->p_next )
931 int i_glyph_tmax = 0;
932 int i_bitmap_offset, i_offset, i_align_offset = 0;
933 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
935 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
936 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
939 if( p_line->i_width < i_width )
941 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
943 i_align_offset = i_width - p_line->i_width;
945 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
947 i_align_offset = ( i_width - p_line->i_width ) / 2;
951 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
953 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
955 i_offset = ( p_line->p_glyph_pos[ i ].y +
956 i_glyph_tmax - p_glyph->top + 3 ) *
957 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
960 if( p_line->b_new_color_mode )
962 /* Every glyph can (and in fact must) have its own color */
963 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
966 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
968 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
970 uint8_t i_y_local = i_y;
971 uint8_t i_u_local = i_u;
972 uint8_t i_v_local = i_v;
974 if( p_line->p_fg_bg_ratio != 0x00 )
976 int i_split = p_glyph->bitmap.width *
977 p_line->p_fg_bg_ratio[ i ] / 0x7f;
981 YUVFromRGB( p_line->p_bg_rgb[ i ],
982 &i_y_local, &i_u_local, &i_v_local );
986 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
988 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
989 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
991 p_dst_u[i_offset+x] = i_u;
992 p_dst_v[i_offset+x] = i_v;
994 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
995 p_dst_a[i_offset+x] = 0xff;
1001 if( p_line->pi_underline_thickness[ i ] )
1003 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1004 p_line->pi_underline_offset[ i ],
1005 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1006 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1007 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1008 i_glyph_tmax, i_align_offset,
1015 /* Apply the alpha setting */
1016 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1017 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1023 * This function renders a text subpicture region into another one.
1024 * It also calculates the size needed for this string, and renders the
1025 * needed glyphs into memory. It is used as pf_add_string callback in
1026 * the vout method by this module
1028 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1029 subpicture_region_t *p_region_in )
1031 filter_sys_t *p_sys = p_filter->p_sys;
1032 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1033 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1034 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1035 size_t i_string_length;
1037 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1047 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1048 psz_string = p_region_in->psz_text;
1049 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1051 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1052 i_scale = val.i_int;
1054 if( p_region_in->p_style )
1056 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1057 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1058 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1062 i_font_color = p_sys->i_font_color;
1063 i_font_alpha = 255 - p_sys->i_font_opacity;
1064 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1067 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1068 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1069 SetFontSize( p_filter, i_font_size );
1071 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1072 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1073 i_blue = i_font_color & 0x000000FF;
1075 result.x = result.y = 0;
1076 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1078 #if defined(WORDS_BIGENDIAN)
1079 psz_unicode = ToCharset( "UCS-4BE", psz_string, &i_string_length );
1081 psz_unicode = ToCharset( "UCS-4LE", psz_string, &i_string_length );
1083 if( psz_unicode == NULL )
1085 psz_unicode_orig = psz_unicode;
1086 i_string_length /= 4;
1088 #if defined(HAVE_FRIBIDI)
1090 uint32_t *p_fribidi_string;
1091 int32_t start_pos, pos = 0;
1093 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1094 if( !p_fribidi_string )
1097 /* Do bidi conversion line-by-line */
1098 while( pos < i_string_length )
1100 while( pos < i_string_length )
1102 i_char = psz_unicode[pos];
1103 if (i_char != '\r' && i_char != '\n')
1105 p_fribidi_string[pos] = i_char;
1109 while( pos < i_string_length )
1111 i_char = psz_unicode[pos];
1112 if (i_char == '\r' || i_char == '\n')
1116 if (pos > start_pos)
1118 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1119 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1122 (FriBidiChar*)p_fribidi_string + start_pos,
1127 free( psz_unicode_orig );
1128 psz_unicode = psz_unicode_orig = p_fribidi_string;
1129 p_fribidi_string[ i_string_length ] = 0;
1133 /* Calculate relative glyph positions and a bounding box for the
1135 if( !(p_line = NewLine( strlen( psz_string ))) )
1138 i_pen_x = i_pen_y = 0;
1140 psz_line_start = psz_unicode;
1142 #define face p_sys->p_face
1143 #define glyph face->glyph
1145 while( *psz_unicode )
1147 i_char = *psz_unicode++;
1148 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1153 if( i_char == '\n' )
1155 psz_line_start = psz_unicode;
1156 if( !(p_next = NewLine( strlen( psz_string ))) )
1158 p_line->p_next = p_next;
1159 p_line->i_width = line.xMax;
1160 p_line->i_height = face->size->metrics.height >> 6;
1161 p_line->pp_glyphs[ i ] = NULL;
1162 p_line->i_alpha = i_font_alpha;
1163 p_line->i_red = i_red;
1164 p_line->i_green = i_green;
1165 p_line->i_blue = i_blue;
1168 result.x = __MAX( result.x, line.xMax );
1169 result.y += face->size->metrics.height >> 6;
1172 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1173 i_pen_y += face->size->metrics.height >> 6;
1175 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1180 i_glyph_index = FT_Get_Char_Index( face, i_char );
1181 if( p_sys->i_use_kerning && i_glyph_index
1185 FT_Get_Kerning( face, i_previous, i_glyph_index,
1186 ft_kerning_default, &delta );
1187 i_pen_x += delta.x >> 6;
1190 p_line->p_glyph_pos[ i ].x = i_pen_x;
1191 p_line->p_glyph_pos[ i ].y = i_pen_y;
1192 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1195 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1198 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1203 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1206 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1210 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1211 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1214 FT_Done_Glyph( tmp_glyph );
1217 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1220 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1221 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1222 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1224 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1225 p_line->pp_glyphs[ i ] = NULL;
1227 p_line = NewLine( strlen( psz_string ));
1228 if( p_prev ) p_prev->p_next = p_line;
1229 else p_lines = p_line;
1231 uint32_t *psz_unicode_saved = psz_unicode;
1232 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1236 if( psz_unicode == psz_line_start )
1237 { /* try harder to break that line */
1238 psz_unicode = psz_unicode_saved;
1239 while( psz_unicode > psz_line_start &&
1240 *psz_unicode != '_' && *psz_unicode != '/' &&
1241 *psz_unicode != '\\' && *psz_unicode != '.' )
1246 if( psz_unicode == psz_line_start )
1248 msg_Warn( p_filter, "unbreakable string" );
1253 *psz_unicode = '\n';
1255 psz_unicode = psz_line_start;
1258 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1261 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1262 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1264 i_previous = i_glyph_index;
1265 i_pen_x += glyph->advance.x >> 6;
1269 p_line->i_width = line.xMax;
1270 p_line->i_height = face->size->metrics.height >> 6;
1271 p_line->pp_glyphs[ i ] = NULL;
1272 p_line->i_alpha = i_font_alpha;
1273 p_line->i_red = i_red;
1274 p_line->i_green = i_green;
1275 p_line->i_blue = i_blue;
1276 result.x = __MAX( result.x, line.xMax );
1277 result.y += line.yMax - line.yMin;
1282 p_region_out->i_x = p_region_in->i_x;
1283 p_region_out->i_y = p_region_in->i_y;
1285 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1286 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1288 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1290 free( psz_unicode_orig );
1291 FreeLines( p_lines );
1295 free( psz_unicode_orig );
1296 FreeLines( p_lines );
1297 return VLC_EGENERIC;
1300 #ifdef HAVE_FONTCONFIG
1301 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1302 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1303 bool b_italic, bool b_uline, bool b_through )
1305 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1309 p_style->i_font_size = i_font_size;
1310 p_style->i_font_color = i_font_color;
1311 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1312 p_style->b_italic = b_italic;
1313 p_style->b_bold = b_bold;
1314 p_style->b_underline = b_uline;
1315 p_style->b_through = b_through;
1317 p_style->psz_fontname = strdup( psz_fontname );
1322 static void DeleteStyle( ft_style_t *p_style )
1326 free( p_style->psz_fontname );
1331 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1338 if(( s1->i_font_size == s2->i_font_size ) &&
1339 ( s1->i_font_color == s2->i_font_color ) &&
1340 ( s1->b_italic == s2->b_italic ) &&
1341 ( s1->b_through == s2->b_through ) &&
1342 ( s1->b_bold == s2->b_bold ) &&
1343 ( s1->b_underline == s2->b_underline ) &&
1344 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1351 static void IconvText( filter_t *p_filter, const char *psz_string,
1352 size_t *i_string_length, uint32_t **ppsz_unicode )
1354 /* If memory hasn't been allocated for our output string, allocate it here
1355 * - the calling function must now be responsible for freeing it.
1357 if( !*ppsz_unicode )
1358 *ppsz_unicode = (uint32_t *)
1359 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1361 /* We don't need to handle a NULL pointer in *ppsz_unicode
1362 * if we are instead testing for a non NULL value like we are here */
1367 #if defined(WORDS_BIGENDIAN)
1368 ToCharset( "UCS-4BE", psz_string, i_string_length );
1370 ToCharset( "UCS-4LE", psz_string, i_string_length );
1372 if( *ppsz_unicode != NULL )
1373 *i_string_length /= 4;
1375 /* FIXME: This is going to fail miserably in the caller */
1376 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1380 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1381 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1382 bool b_uline, bool b_through )
1384 ft_style_t *p_style = NULL;
1386 char *psz_fontname = NULL;
1387 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1388 uint32_t i_karaoke_bg_color = i_font_color;
1389 int i_font_size = p_sys->i_font_size;
1391 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1392 &i_font_color, &i_karaoke_bg_color ))
1394 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1395 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1400 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1401 bool b_uline, bool b_through, bool b_bold,
1402 bool b_italic, int i_karaoke_bgcolor,
1403 line_desc_t *p_line, uint32_t *psz_unicode,
1404 int *pi_pen_x, int i_pen_y, int *pi_start,
1405 FT_Vector *p_result )
1410 bool b_first_on_line = true;
1413 int i_pen_x_start = *pi_pen_x;
1415 uint32_t *psz_unicode_start = psz_unicode;
1417 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1419 /* Account for part of line already in position */
1420 for( i=0; i<*pi_start; i++ )
1424 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1425 ft_glyph_bbox_pixels, &glyph_size );
1427 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1428 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1429 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1430 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1436 b_first_on_line = false;
1438 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1444 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1445 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1449 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1450 ft_kerning_default, &delta );
1451 *pi_pen_x += delta.x >> 6;
1453 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1454 p_line->p_glyph_pos[ i ].y = i_pen_y;
1456 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1459 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1463 "unable to render text FT_Load_Glyph returned %d", i_error );
1464 p_line->pp_glyphs[ i ] = NULL;
1465 return VLC_EGENERIC;
1469 /* Do synthetic styling now that Freetype supports it;
1470 * ie. if the font we have loaded is NOT already in the
1471 * style that the tags want, then switch it on; if they
1472 * are then don't. */
1473 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1474 FT_GlyphSlot_Embolden( p_face->glyph );
1475 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1476 FT_GlyphSlot_Oblique( p_face->glyph );
1478 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1482 "unable to render text FT_Get_Glyph returned %d", i_error );
1483 p_line->pp_glyphs[ i ] = NULL;
1484 return VLC_EGENERIC;
1486 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1487 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1490 FT_Done_Glyph( tmp_glyph );
1493 if( b_uline || b_through )
1495 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1496 p_face->size->metrics.y_scale));
1497 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1498 p_face->size->metrics.y_scale));
1500 p_line->pi_underline_offset[ i ] =
1501 ( aOffset < 0 ) ? -aOffset : aOffset;
1502 p_line->pi_underline_thickness[ i ] =
1503 ( aSize < 0 ) ? -aSize : aSize;
1506 /* Move the baseline to make it strikethrough instead of
1507 * underline. That means that strikethrough takes precedence
1509 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1510 p_face->size->metrics.y_scale));
1512 p_line->pi_underline_offset[ i ] -=
1513 ( aDescent < 0 ) ? -aDescent : aDescent;
1517 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1518 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1519 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1520 p_line->p_fg_bg_ratio[ i ] = 0x00;
1522 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1523 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1524 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1526 for( ; i >= *pi_start; i-- )
1527 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1530 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1534 if( psz_unicode == psz_unicode_start )
1536 if( b_first_on_line )
1538 msg_Warn( p_filter, "unbreakable string" );
1539 p_line->pp_glyphs[ i ] = NULL;
1540 return VLC_EGENERIC;
1542 *pi_pen_x = i_pen_x_start;
1544 p_line->i_width = line.xMax;
1545 p_line->i_height = __MAX( p_line->i_height,
1546 p_face->size->metrics.height >> 6 );
1547 p_line->pp_glyphs[ i ] = NULL;
1549 p_result->x = __MAX( p_result->x, line.xMax );
1550 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1551 i_yMax - i_yMin ) );
1556 *psz_unicode = '\n';
1558 psz_unicode = psz_unicode_start;
1559 *pi_pen_x = i_pen_x_start;
1567 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1568 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1570 i_previous = i_glyph_index;
1571 *pi_pen_x += p_face->glyph->advance.x >> 6;
1574 p_line->i_width = line.xMax;
1575 p_line->i_height = __MAX( p_line->i_height,
1576 p_face->size->metrics.height >> 6 );
1577 p_line->pp_glyphs[ i ] = NULL;
1579 p_result->x = __MAX( p_result->x, line.xMax );
1580 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1581 line.yMax - line.yMin ) );
1585 /* Get rid of any text processed - if necessary repositioning
1586 * at the start of a new line of text
1590 *psz_unicode_start = '\0';
1592 else if( psz_unicode > psz_unicode_start )
1594 for( i=0; psz_unicode[ i ]; i++ )
1595 psz_unicode_start[ i ] = psz_unicode[ i ];
1596 psz_unicode_start[ i ] = '\0';
1602 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1603 uint32_t **psz_text_out, uint32_t *pi_runs,
1604 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1605 ft_style_t *p_style )
1607 size_t i_string_length;
1609 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1610 *psz_text_out += i_string_length;
1612 if( ppp_styles && ppi_run_lengths )
1616 /* XXX this logic looks somewhat broken */
1620 *ppp_styles = realloc_or_free( *ppp_styles,
1621 *pi_runs * sizeof( ft_style_t * ) );
1623 else if( *pi_runs == 1 )
1625 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1628 /* We have just malloc'ed this memory successfully -
1629 * *pi_runs HAS to be within the memory area of *ppp_styles */
1632 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1636 /* XXX more iffy logic */
1638 if( *ppi_run_lengths )
1640 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1641 *pi_runs * sizeof( uint32_t ) );
1643 else if( *pi_runs == 1 )
1645 *ppi_run_lengths = (uint32_t *)
1646 malloc( *pi_runs * sizeof( uint32_t ) );
1649 /* same remarks here */
1650 if( *ppi_run_lengths )
1652 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1655 /* If we couldn't use the p_style argument due to memory allocation
1656 * problems above, release it here.
1658 if( p_style ) DeleteStyle( p_style );
1661 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1665 for( k=0; k < p_sys->i_font_attachments; k++ )
1667 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1669 FT_Face p_face = NULL;
1671 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1679 bool match = !strcasecmp( p_face->family_name,
1680 p_style->psz_fontname );
1682 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1683 match = match && p_style->b_bold;
1685 match = match && !p_style->b_bold;
1687 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1688 match = match && p_style->b_italic;
1690 match = match && !p_style->b_italic;
1698 FT_Done_Face( p_face );
1703 return VLC_EGENERIC;
1706 static int ProcessLines( filter_t *p_filter,
1711 uint32_t *pi_run_lengths,
1712 ft_style_t **pp_styles,
1713 line_desc_t **pp_lines,
1715 FT_Vector *p_result,
1719 uint32_t *pi_k_run_lengths,
1720 uint32_t *pi_k_durations )
1722 filter_sys_t *p_sys = p_filter->p_sys;
1723 ft_style_t **pp_char_styles;
1724 int *p_new_positions = NULL;
1725 int8_t *p_levels = NULL;
1726 uint8_t *pi_karaoke_bar = NULL;
1730 /* Assign each character in the text string its style explicitly, so that
1731 * after the characters have been shuffled around by Fribidi, we can re-apply
1732 * the styles, and to simplify the calculation of runs within a line.
1734 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1735 if( !pp_char_styles )
1740 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1741 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1742 * we just won't be able to display the progress bar; at least we'll
1748 for( j = 0; j < i_runs; j++ )
1749 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1750 pp_char_styles[ i++ ] = pp_styles[ j ];
1752 #if defined(HAVE_FRIBIDI)
1754 ft_style_t **pp_char_styles_new;
1755 int *p_old_positions;
1756 uint32_t *p_fribidi_string;
1757 int start_pos, pos = 0;
1759 pp_char_styles_new = (ft_style_t **)
1760 malloc( i_len * sizeof( ft_style_t * ));
1762 p_fribidi_string = (uint32_t *)
1763 malloc( (i_len + 1) * sizeof(uint32_t) );
1764 p_old_positions = (int *)
1765 malloc( (i_len + 1) * sizeof( int ) );
1766 p_new_positions = (int *)
1767 malloc( (i_len + 1) * sizeof( int ) );
1768 p_levels = (int8_t *)
1769 malloc( (i_len + 1) * sizeof( int8_t ) );
1771 if( ! pp_char_styles_new ||
1772 ! p_fribidi_string ||
1773 ! p_old_positions ||
1774 ! p_new_positions ||
1778 free( p_old_positions );
1779 free( p_new_positions );
1780 free( p_fribidi_string );
1781 free( pp_char_styles_new );
1782 free( pi_karaoke_bar );
1784 free( pp_char_styles );
1788 /* Do bidi conversion line-by-line */
1791 while(pos < i_len) {
1792 if (psz_text[pos] != '\n')
1794 p_fribidi_string[pos] = psz_text[pos];
1795 pp_char_styles_new[pos] = pp_char_styles[pos];
1796 p_new_positions[pos] = pos;
1801 while(pos < i_len) {
1802 if (psz_text[pos] == '\n')
1806 if (pos > start_pos)
1808 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1809 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1810 pos - start_pos, &base_dir,
1811 (FriBidiChar*)p_fribidi_string + start_pos,
1812 p_new_positions + start_pos,
1814 p_levels + start_pos );
1815 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1817 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1818 p_old_positions[ j - start_pos ] ];
1819 p_new_positions[ j ] += start_pos;
1823 free( p_old_positions );
1824 free( pp_char_styles );
1825 pp_char_styles = pp_char_styles_new;
1826 psz_text = p_fribidi_string;
1827 p_fribidi_string[ i_len ] = 0;
1830 /* Work out the karaoke */
1831 if( pi_karaoke_bar )
1833 int64_t i_last_duration = 0;
1834 int64_t i_duration = 0;
1835 int64_t i_start_pos = 0;
1836 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1838 for( k = 0; k< i_k_runs; k++ )
1840 double fraction = 0.0;
1842 i_duration += pi_k_durations[ k ];
1844 if( i_duration < i_elapsed )
1846 /* Completely finished this run-length -
1847 * let it render normally */
1851 else if( i_elapsed < i_last_duration )
1853 /* Haven't got up to this segment yet -
1854 * render it completely in karaoke BG mode */
1860 /* Partway through this run */
1862 fraction = (double)(i_elapsed - i_last_duration) /
1863 (double)pi_k_durations[ k ];
1865 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1867 double shade = pi_k_run_lengths[ k ] * fraction;
1869 if( p_new_positions )
1870 j = p_new_positions[ i_start_pos + i ];
1872 j = i_start_pos + i;
1874 if( i < (uint32_t)shade )
1875 pi_karaoke_bar[ j ] = 0xff;
1876 else if( (double)i > shade )
1877 pi_karaoke_bar[ j ] = 0x00;
1880 shade -= (int)shade;
1881 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1882 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1886 i_last_duration = i_duration;
1887 i_start_pos += pi_k_run_lengths[ k ];
1891 free( p_new_positions );
1893 FT_Vector tmp_result;
1895 line_desc_t *p_line = NULL;
1896 line_desc_t *p_prev = NULL;
1902 p_result->x = p_result->y = 0;
1903 tmp_result.x = tmp_result.y = 0;
1906 for( k = 0; k <= (uint32_t) i_len; k++ )
1908 if( ( k == (uint32_t) i_len ) ||
1910 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1912 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1914 /* End of the current style run */
1915 FT_Face p_face = NULL;
1918 /* Look for a match amongst our attachments first */
1919 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1923 char *psz_fontfile = NULL;
1925 psz_fontfile = FontConfig_Select( NULL,
1926 p_style->psz_fontname,
1930 if( psz_fontfile && ! *psz_fontfile )
1932 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1933 " so using default font", p_style->psz_fontname,
1934 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1935 (p_style->b_bold ? "(Bold)" :
1936 (p_style->b_italic ? "(Italic)" : ""))) );
1937 free( psz_fontfile );
1938 psz_fontfile = NULL;
1943 if( FT_New_Face( p_sys->p_library,
1944 psz_fontfile, i_idx, &p_face ) )
1946 free( psz_fontfile );
1947 free( pp_char_styles );
1948 #if defined(HAVE_FRIBIDI)
1951 free( pi_karaoke_bar );
1952 return VLC_EGENERIC;
1954 free( psz_fontfile );
1958 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1960 /* We've loaded a font face which is unhelpful for actually
1961 * rendering text - fallback to the default one.
1963 FT_Done_Face( p_face );
1967 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1968 ft_encoding_unicode ) ||
1969 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1970 p_style->i_font_size ) )
1972 if( p_face ) FT_Done_Face( p_face );
1973 free( pp_char_styles );
1974 #if defined(HAVE_FRIBIDI)
1977 free( pi_karaoke_bar );
1978 return VLC_EGENERIC;
1980 p_sys->i_use_kerning =
1981 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1984 uint32_t *psz_unicode = (uint32_t *)
1985 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1988 if( p_face ) FT_Done_Face( p_face );
1989 free( pp_char_styles );
1990 free( psz_unicode );
1991 #if defined(HAVE_FRIBIDI)
1994 free( pi_karaoke_bar );
1997 memcpy( psz_unicode, psz_text + i_prev,
1998 sizeof( uint32_t ) * ( k - i_prev ) );
1999 psz_unicode[ k - i_prev ] = 0;
2000 while( *psz_unicode )
2004 if( !(p_line = NewLine( i_len - i_prev)) )
2006 if( p_face ) FT_Done_Face( p_face );
2007 free( pp_char_styles );
2008 free( psz_unicode );
2009 #if defined(HAVE_FRIBIDI)
2012 free( pi_karaoke_bar );
2015 /* New Color mode only works in YUVA rendering mode --
2016 * (RGB mode has palette constraints on it). We therefore
2017 * need to populate the legacy colour fields also.
2019 p_line->b_new_color_mode = true;
2020 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2021 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2022 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2023 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2024 p_line->p_next = NULL;
2026 i_pen_y += tmp_result.y;
2030 if( p_prev ) p_prev->p_next = p_line;
2031 else *pp_lines = p_line;
2034 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2035 p_style->i_font_color, p_style->b_underline,
2039 p_style->i_karaoke_bg_color,
2040 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2041 &tmp_result ) != VLC_SUCCESS )
2043 if( p_face ) FT_Done_Face( p_face );
2044 free( pp_char_styles );
2045 free( psz_unicode );
2046 #if defined(HAVE_FRIBIDI)
2049 free( pi_karaoke_bar );
2050 return VLC_EGENERIC;
2055 p_result->x = __MAX( p_result->x, tmp_result.x );
2056 p_result->y += tmp_result.y;
2061 if( *psz_unicode == '\n')
2065 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2067 *c_ptr = *(c_ptr+1);
2072 free( psz_unicode );
2073 if( p_face ) FT_Done_Face( p_face );
2077 free( pp_char_styles );
2078 #if defined(HAVE_FRIBIDI)
2083 p_result->x = __MAX( p_result->x, tmp_result.x );
2084 p_result->y += tmp_result.y;
2087 if( pi_karaoke_bar )
2090 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2092 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2094 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2098 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2100 /* 100% BG colour will render faster if we
2101 * instead make it 100% FG colour, so leave
2102 * the ratio alone and copy the value across
2104 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2108 if( pi_karaoke_bar[ i ] & 0x80 )
2110 /* Swap Left and Right sides over for Right aligned
2111 * language text (eg. Arabic, Hebrew)
2113 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2115 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2116 p_line->p_bg_rgb[ k ] = i_tmp;
2118 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2121 /* Jump over the '\n' at the line-end */
2124 free( pi_karaoke_bar );
2130 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2131 subpicture_region_t *p_region_in )
2133 int rv = VLC_SUCCESS;
2134 stream_t *p_sub = NULL;
2136 if( !p_region_in || !p_region_in->psz_html )
2137 return VLC_EGENERIC;
2139 /* Reset the default fontsize in case screen metrics have changed */
2140 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2142 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2143 (uint8_t *) p_region_in->psz_html,
2144 strlen( p_region_in->psz_html ),
2146 if( unlikely(p_sub == NULL) )
2149 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2150 bool b_karaoke = false;
2153 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2155 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2157 p_filter->p_sys->p_xml = p_xml_reader;
2160 /* Look for Root Node */
2161 if( xml_ReaderRead( p_xml_reader ) == 1 )
2163 char *psz_node = xml_ReaderName( p_xml_reader );
2165 if( !strcasecmp( "karaoke", psz_node ) )
2167 /* We're going to have to render the text a number
2168 * of times to show the progress marker on the text.
2170 var_SetBool( p_filter, "text-rerender", true );
2173 else if( !strcasecmp( "text", psz_node ) )
2179 /* Only text and karaoke tags are supported */
2180 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2181 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2182 p_xml_reader = NULL;
2194 uint32_t i_runs = 0;
2195 uint32_t i_k_runs = 0;
2196 uint32_t *pi_run_lengths = NULL;
2197 uint32_t *pi_k_run_lengths = NULL;
2198 uint32_t *pi_k_durations = NULL;
2199 ft_style_t **pp_styles = NULL;
2201 line_desc_t *p_lines = NULL;
2203 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2204 sizeof( uint32_t ) );
2207 rv = ProcessNodes( p_filter, p_xml_reader,
2208 p_region_in->p_style, psz_text, &i_len,
2209 &i_runs, &pi_run_lengths, &pp_styles,
2210 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2213 p_region_out->i_x = p_region_in->i_x;
2214 p_region_out->i_y = p_region_in->i_y;
2216 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2218 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2219 pi_run_lengths, pp_styles, &p_lines,
2220 &result, b_karaoke, i_k_runs,
2221 pi_k_run_lengths, pi_k_durations );
2224 for( uint_fast32_t k=0; k<i_runs; k++)
2225 DeleteStyle( pp_styles[k] );
2227 free( pi_run_lengths );
2230 /* Don't attempt to render text that couldn't be layed out
2232 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2234 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2235 Render( p_filter, p_region_out, p_lines,
2236 result.x, result.y );
2238 RenderYUVA( p_filter, p_region_out, p_lines,
2239 result.x, result.y );
2242 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2243 FreeLines( p_lines );
2245 stream_Delete( p_sub );
2249 static char* FontConfig_Select( FcConfig* priv, const char* family,
2250 bool b_bold, bool b_italic, int *i_idx )
2253 FcPattern *pat, *p_pat;
2257 pat = FcPatternCreate();
2258 if (!pat) return NULL;
2260 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2261 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2262 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2263 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2265 FcDefaultSubstitute( pat );
2267 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2269 FcPatternDestroy( pat );
2273 p_pat = FcFontMatch( priv, pat, &result );
2274 FcPatternDestroy( pat );
2275 if( !p_pat ) return NULL;
2277 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2278 || ( val_b != FcTrue ) )
2280 FcPatternDestroy( p_pat );
2283 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2288 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2290 FcPatternDestroy( p_pat );
2295 if( strcasecmp((const char*)val_s, family ) != 0 )
2296 msg_Warn( p_filter, "fontconfig: selected font family is not"
2297 "the requested one: '%s' != '%s'\n",
2298 (const char*)val_s, family );
2301 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2303 FcPatternDestroy( p_pat );
2307 FcPatternDestroy( p_pat );
2308 return strdup( (const char*)val_s );
2312 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2313 uint32_t **psz_text_out, uint32_t *pi_runs,
2314 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2315 ft_style_t *p_style )
2317 VLC_UNUSED(p_filter);
2318 VLC_UNUSED(psz_text_in);
2319 VLC_UNUSED(psz_text_out);
2320 VLC_UNUSED(pi_runs);
2321 VLC_UNUSED(ppi_run_lengths);
2322 VLC_UNUSED(ppp_styles);
2323 VLC_UNUSED(p_style);
2326 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2327 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2328 bool b_uline, bool b_through )
2331 VLC_UNUSED(p_fonts);
2333 VLC_UNUSED(b_italic);
2334 VLC_UNUSED(b_uline);
2335 VLC_UNUSED(b_through);
2340 static void FreeLine( line_desc_t *p_line )
2343 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2345 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2347 free( p_line->pp_glyphs );
2348 free( p_line->p_glyph_pos );
2349 free( p_line->p_fg_rgb );
2350 free( p_line->p_bg_rgb );
2351 free( p_line->p_fg_bg_ratio );
2352 free( p_line->pi_underline_offset );
2353 free( p_line->pi_underline_thickness );
2357 static void FreeLines( line_desc_t *p_lines )
2359 line_desc_t *p_line, *p_next;
2361 if( !p_lines ) return;
2363 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2365 p_next = p_line->p_next;
2370 static line_desc_t *NewLine( int i_count )
2372 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2374 if( !p_line ) return NULL;
2375 p_line->i_height = 0;
2376 p_line->i_width = 0;
2377 p_line->p_next = NULL;
2379 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2380 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2381 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2382 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2383 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2384 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2385 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2386 if( ( p_line->pp_glyphs == NULL ) ||
2387 ( p_line->p_glyph_pos == NULL ) ||
2388 ( p_line->p_fg_rgb == NULL ) ||
2389 ( p_line->p_bg_rgb == NULL ) ||
2390 ( p_line->p_fg_bg_ratio == NULL ) ||
2391 ( p_line->pi_underline_offset == NULL ) ||
2392 ( p_line->pi_underline_thickness == NULL ) )
2394 free( p_line->pi_underline_thickness );
2395 free( p_line->pi_underline_offset );
2396 free( p_line->p_fg_rgb );
2397 free( p_line->p_bg_rgb );
2398 free( p_line->p_fg_bg_ratio );
2399 free( p_line->p_glyph_pos );
2400 free( p_line->pp_glyphs );
2404 p_line->pp_glyphs[0] = NULL;
2405 p_line->b_new_color_mode = false;
2410 static int GetFontSize( filter_t *p_filter )
2412 filter_sys_t *p_sys = p_filter->p_sys;
2416 if( p_sys->i_default_font_size )
2418 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2419 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2421 i_size = p_sys->i_default_font_size;
2425 var_Get( p_filter, "freetype-rel-fontsize", &val );
2428 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2429 p_filter->p_sys->i_display_height =
2430 p_filter->fmt_out.video.i_height;
2435 msg_Warn( p_filter, "invalid fontsize, using 12" );
2436 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2437 i_size = 12 * val.i_int / 1000;
2444 static int SetFontSize( filter_t *p_filter, int i_size )
2446 filter_sys_t *p_sys = p_filter->p_sys;
2450 i_size = GetFontSize( p_filter );
2452 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2455 p_sys->i_font_size = i_size;
2457 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2459 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2460 return VLC_EGENERIC;
2466 static void YUVFromRGB( uint32_t i_argb,
2467 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2469 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2470 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2471 int i_blue = ( i_argb & 0x000000ff );
2473 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2474 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2475 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2476 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2477 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2478 -585 * i_blue + 4096 + 1048576) >> 13, 240);