1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
42 #include <vlc_dialog.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
52 #define FT_MulFix(v, s) (((v)*(s))>>16)
55 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
56 #define FC_DEFAULT_FONT "Lucida Grande"
57 #elif defined( SYS_BEOS )
58 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
59 #define FC_DEFAULT_FONT "Swiss"
60 #elif defined( WIN32 )
61 #define DEFAULT_FONT "" /* Default font found at run-time */
62 #define FC_DEFAULT_FONT "Arial"
64 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
65 #define FC_DEFAULT_FONT "Serif Bold"
68 #if defined(HAVE_FRIBIDI)
69 #include <fribidi/fribidi.h>
72 #ifdef HAVE_FONTCONFIG
73 #include <fontconfig/fontconfig.h>
75 #define DEFAULT_FONT FC_DEFAULT_FONT
80 /*****************************************************************************
82 *****************************************************************************/
83 static int Create ( vlc_object_t * );
84 static void Destroy( vlc_object_t * );
86 #define FONT_TEXT N_("Font")
88 #ifdef HAVE_FONTCONFIG
89 #define FONT_LONGTEXT N_("Font family for the font you want to use")
91 #define FONT_LONGTEXT N_("Fontfile for the font you want to use")
94 #define FONTSIZE_TEXT N_("Font size in pixels")
95 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
96 "that will be rendered on the video. " \
97 "If set to something different than 0 this option will override the " \
98 "relative font size." )
99 #define OPACITY_TEXT N_("Opacity")
100 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
101 "text that will be rendered on the video. 0 = transparent, " \
102 "255 = totally opaque. " )
103 #define COLOR_TEXT N_("Text default color")
104 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
105 "the video. This must be an hexadecimal (like HTML colors). The first two "\
106 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
107 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
108 #define FONTSIZER_TEXT N_("Relative font size")
109 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
110 "fonts that will be rendered on the video. If absolute font size is set, "\
111 "relative size will be overriden." )
113 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
114 static const char *const ppsz_sizes_text[] = {
115 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
116 #define YUVP_TEXT N_("Use YUVP renderer")
117 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
118 "This option is only needed if you want to encode into DVB subtitles" )
119 #define EFFECT_TEXT N_("Font Effect")
120 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
121 "text to improve its readability." )
123 #define EFFECT_BACKGROUND 1
124 #define EFFECT_OUTLINE 2
125 #define EFFECT_OUTLINE_FAT 3
127 static int const pi_effects[] = { 1, 2, 3 };
128 static const char *const ppsz_effects_text[] = {
129 N_("Background"),N_("Outline"), N_("Fat Outline") };
130 static const int pi_color_values[] = {
131 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
132 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
133 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
135 static const char *const ppsz_color_descriptions[] = {
136 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
137 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
138 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
141 set_shortname( N_("Text renderer"))
142 set_description( N_("Freetype2 font renderer") )
143 set_category( CAT_VIDEO )
144 set_subcategory( SUBCAT_VIDEO_SUBPIC )
146 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
149 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
150 FONTSIZE_LONGTEXT, true )
152 /* opacity valid on 0..255, with default 255 = fully opaque */
153 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
154 OPACITY_TEXT, OPACITY_LONGTEXT, true )
156 /* hook to the color values list, with default 0x00ffffff = white */
157 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
158 COLOR_LONGTEXT, false )
159 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
161 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
162 FONTSIZER_LONGTEXT, false )
163 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
164 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
165 EFFECT_LONGTEXT, false )
166 change_integer_list( pi_effects, ppsz_effects_text, NULL )
168 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
169 YUVP_LONGTEXT, true )
170 set_capability( "text renderer", 100 )
171 add_shortcut( "text" )
172 set_callbacks( Create, Destroy )
177 /*****************************************************************************
179 *****************************************************************************/
181 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
182 static int RenderText( filter_t *, subpicture_region_t *,
183 subpicture_region_t * );
184 #ifdef HAVE_FONTCONFIG
185 static int RenderHtml( filter_t *, subpicture_region_t *,
186 subpicture_region_t * );
187 static char *FontConfig_Select( FcConfig *, const char *,
192 static int LoadFontsFromAttachments( filter_t *p_filter );
194 static int GetFontSize( filter_t *p_filter );
195 static int SetFontSize( filter_t *, int );
196 static void YUVFromRGB( uint32_t i_argb,
197 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
199 typedef struct line_desc_t line_desc_t;
202 /** NULL-terminated list of glyphs making the string */
203 FT_BitmapGlyph *pp_glyphs;
204 /** list of relative positions for the glyphs */
205 FT_Vector *p_glyph_pos;
206 /** list of RGB information for styled text
207 * -- if the rendering mode supports it (RenderYUVA) and
208 * b_new_color_mode is set, then it becomes possible to
209 * have multicoloured text within the subtitles. */
212 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
213 bool b_new_color_mode;
214 /** underline information -- only supplied if text should be underlined */
215 uint16_t *pi_underline_offset;
216 uint16_t *pi_underline_thickness;
220 int i_red, i_green, i_blue;
225 static line_desc_t *NewLine( int );
230 uint32_t i_font_color; /* ARGB */
231 uint32_t i_karaoke_bg_color; /* ARGB */
238 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
239 static void FreeLines( line_desc_t * );
240 static void FreeLine( line_desc_t * );
242 /*****************************************************************************
243 * filter_sys_t: freetype local data
244 *****************************************************************************
245 * This structure is part of the video output thread descriptor.
246 * It describes the freetype specific properties of an output thread.
247 *****************************************************************************/
250 FT_Library p_library; /* handle to library */
251 FT_Face p_face; /* handle to face object */
253 uint8_t i_font_opacity;
258 int i_default_font_size;
259 int i_display_height;
260 #ifdef HAVE_FONTCONFIG
261 char* psz_fontfamily;
265 input_attachment_t **pp_font_attachments;
266 int i_font_attachments;
270 #define UCHAR uint32_t
271 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
272 #define TR_FONT_STYLE_PTR ft_style_t *
274 #include "text_renderer.h"
276 /*****************************************************************************
277 * Create: allocates osd-text video thread output method
278 *****************************************************************************
279 * This function allocates and initializes a Clone vout method.
280 *****************************************************************************/
281 static int Create( vlc_object_t *p_this )
283 filter_t *p_filter = (filter_t *)p_this;
285 char *psz_fontfile=NULL;
286 char *psz_fontfamily=NULL;
287 int i_error,fontindex;
289 #ifdef HAVE_FONTCONFIG
290 FcPattern *fontpattern = NULL, *fontmatch = NULL;
291 /* Initialise result to Match, as fontconfig doesnt
292 * really set this other than some error-cases */
293 FcResult fontresult = FcResultMatch;
297 /* Allocate structure */
298 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
301 #ifdef HAVE_FONTCONFIG
302 p_sys->psz_fontfamily = NULL;
306 p_sys->p_library = 0;
307 p_sys->i_font_size = 0;
308 p_sys->i_display_height = 0;
310 var_Create( p_filter, "freetype-rel-fontsize",
311 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
313 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
314 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
315 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
316 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
317 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
318 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
319 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
322 if( !psz_fontfamily || !*psz_fontfamily )
324 #ifdef HAVE_FONTCONFIG
325 free( psz_fontfamily);
326 psz_fontfamily=strdup( DEFAULT_FONT );
328 free( psz_fontfamily );
329 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
330 if( !psz_fontfamily )
333 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
334 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
336 strcpy( psz_fontfamily, DEFAULT_FONT );
338 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
342 #ifdef HAVE_FONTCONFIG
343 /* Lets find some fontfile from freetype-font variable family */
345 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
349 dialog_progress_bar_t *p_dialog = dialog_ProgressCreate( p_filter,
350 _("Building font cache"),
351 _("Please wait while your font cache is rebuilt.\n"
352 "This should take less than few minutes."), NULL );
354 path = (char *)malloc( PATH_MAX + 1 );
355 /* Fontconfig doesnt seem to know where windows fonts are with
356 * current contribs. So just tell default windows font directory
357 * is the place to search fonts
359 GetWindowsDirectory( path, PATH_MAX + 1 );
360 strcat( path, "\\fonts" );
362 dialog_ProgressSet( p_dialog, NULL, 0.4 );
364 FcConfigAppFontAddDir( NULL , path );
369 dialog_ProgressSet( p_dialog, NULL, 0.5 );
373 msg_Dbg( p_filter, "Building font database.");
375 FcConfigBuildFonts( NULL );
378 msg_Dbg( p_filter, "Finished building font database." );
379 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
381 fontpattern = FcPatternCreate();
388 dialog_ProgressSet( p_dialog, NULL, 0.7 );
390 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
391 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
392 free( psz_fontsize );
394 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
396 FcDefaultSubstitute( fontpattern );
400 dialog_ProgressSet( p_dialog, NULL, 0.8 );
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 )
409 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
410 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
414 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
415 p_sys->psz_fontfamily = strdup( psz_fontfamily );
419 dialog_ProgressSet( p_dialog, NULL, 1.0 );
420 dialog_ProgressDestroy( p_dialog );
425 p_sys->psz_fontfamily = strdup( DEFAULT_FONT )
426 psz_fontfile = psz_fontfamily;
429 i_error = FT_Init_FreeType( &p_sys->p_library );
432 msg_Err( p_filter, "couldn't initialize freetype" );
436 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
437 fontindex, &p_sys->p_face );
439 if( i_error == FT_Err_Unknown_File_Format )
441 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
446 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
450 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
453 msg_Err( p_filter, "font has no unicode translation table" );
457 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
459 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
462 p_sys->pp_font_attachments = NULL;
463 p_sys->i_font_attachments = 0;
465 p_filter->pf_render_text = RenderText;
466 #ifdef HAVE_FONTCONFIG
467 p_filter->pf_render_html = RenderHtml;
468 FcPatternDestroy( fontmatch );
469 FcPatternDestroy( fontpattern );
471 p_filter->pf_render_html = NULL;
474 free( psz_fontfamily );
475 LoadFontsFromAttachments( p_filter );
480 #ifdef HAVE_FONTCONFIG
481 if( fontmatch ) FcPatternDestroy( fontmatch );
482 if( fontpattern ) FcPatternDestroy( fontpattern );
484 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
485 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
486 free( psz_fontfamily );
491 /*****************************************************************************
492 * Destroy: destroy Clone video thread output method
493 *****************************************************************************
494 * Clean up all data and library connections
495 *****************************************************************************/
496 static void Destroy( vlc_object_t *p_this )
498 filter_t *p_filter = (filter_t *)p_this;
499 filter_sys_t *p_sys = p_filter->p_sys;
501 if( p_sys->pp_font_attachments )
505 for( k = 0; k < p_sys->i_font_attachments; k++ )
506 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
508 free( p_sys->pp_font_attachments );
511 #ifdef HAVE_FONTCONFIG
512 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
513 free( p_sys->psz_fontfamily );
516 /* FcFini asserts calling the subfunction FcCacheFini()
517 * even if no other library functions have been made since FcInit(),
518 * so don't call it. */
520 FT_Done_Face( p_sys->p_face );
521 FT_Done_FreeType( p_sys->p_library );
525 /*****************************************************************************
526 * Make any TTF/OTF fonts present in the attachments of the media file
527 * and store them for later use by the FreeType Engine
528 *****************************************************************************/
529 static int LoadFontsFromAttachments( filter_t *p_filter )
531 filter_sys_t *p_sys = p_filter->p_sys;
532 input_thread_t *p_input;
533 input_attachment_t **pp_attachments;
534 int i_attachments_cnt;
536 int rv = VLC_SUCCESS;
538 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
542 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
544 vlc_object_release(p_input);
548 p_sys->i_font_attachments = 0;
549 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
550 if(! p_sys->pp_font_attachments )
553 for( k = 0; k < i_attachments_cnt; k++ )
555 input_attachment_t *p_attach = pp_attachments[k];
557 if( p_sys->pp_font_attachments )
559 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
560 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
561 ( p_attach->i_data > 0 ) &&
562 ( p_attach->p_data != NULL ) )
564 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
568 vlc_input_attachment_Delete( p_attach );
573 vlc_input_attachment_Delete( p_attach );
576 free( pp_attachments );
578 vlc_object_release(p_input);
583 /*****************************************************************************
584 * Render: place string in picture
585 *****************************************************************************
586 * This function merges the previously rendered freetype glyphs into a picture
587 *****************************************************************************/
588 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
589 line_desc_t *p_line, int i_width, int i_height )
591 static const uint8_t pi_gamma[16] =
592 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
593 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
597 int i, x, y, i_pitch;
598 uint8_t i_y; /* YUV values, derived from incoming RGB */
601 /* Create a new subpicture region */
602 memset( &fmt, 0, sizeof(video_format_t) );
603 fmt.i_chroma = VLC_CODEC_YUVP;
605 fmt.i_width = fmt.i_visible_width = i_width + 4;
606 fmt.i_height = fmt.i_visible_height = i_height + 4;
607 if( p_region->fmt.i_visible_width > 0 )
608 fmt.i_visible_width = p_region->fmt.i_visible_width;
609 if( p_region->fmt.i_visible_height > 0 )
610 fmt.i_visible_height = p_region->fmt.i_visible_height;
611 fmt.i_x_offset = fmt.i_y_offset = 0;
613 assert( !p_region->p_picture );
614 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
615 if( !p_region->p_picture )
619 /* Calculate text color components */
620 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
621 25 * p_line->i_blue + 128) >> 8) + 16;
622 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
623 112 * p_line->i_blue + 128) >> 8) + 128;
624 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
625 18 * p_line->i_blue + 128) >> 8) + 128;
628 fmt.p_palette->i_entries = 16;
629 for( i = 0; i < 8; i++ )
631 fmt.p_palette->palette[i][0] = 0;
632 fmt.p_palette->palette[i][1] = 0x80;
633 fmt.p_palette->palette[i][2] = 0x80;
634 fmt.p_palette->palette[i][3] = pi_gamma[i];
635 fmt.p_palette->palette[i][3] =
636 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
638 for( i = 8; i < fmt.p_palette->i_entries; i++ )
640 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
641 fmt.p_palette->palette[i][1] = i_u;
642 fmt.p_palette->palette[i][2] = i_v;
643 fmt.p_palette->palette[i][3] = pi_gamma[i];
644 fmt.p_palette->palette[i][3] =
645 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
648 p_dst = p_region->p_picture->Y_PIXELS;
649 i_pitch = p_region->p_picture->Y_PITCH;
651 /* Initialize the region pixels */
652 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
654 for( ; p_line != NULL; p_line = p_line->p_next )
656 int i_glyph_tmax = 0;
657 int i_bitmap_offset, i_offset, i_align_offset = 0;
658 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
660 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
661 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
664 if( p_line->i_width < i_width )
666 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
668 i_align_offset = i_width - p_line->i_width;
670 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
672 i_align_offset = ( i_width - p_line->i_width ) / 2;
676 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
678 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
680 i_offset = ( p_line->p_glyph_pos[ i ].y +
681 i_glyph_tmax - p_glyph->top + 2 ) *
682 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
685 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
687 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
689 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
691 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
698 /* Outlining (find something better than nearest neighbour filtering ?) */
701 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
702 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
703 uint8_t left, current;
705 for( y = 1; y < (int)fmt.i_height - 1; y++ )
707 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
708 p_dst += p_region->p_picture->Y_PITCH;
711 for( x = 1; x < (int)fmt.i_width - 1; x++ )
714 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
715 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;
719 memset( p_top, 0, fmt.i_width );
725 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
726 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
727 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
728 int i_glyph_tmax, int i_align_offset,
729 uint8_t i_y, uint8_t i_u, uint8_t i_v,
730 subpicture_region_t *p_region)
734 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
736 p_dst_y = p_region->p_picture->Y_PIXELS;
737 p_dst_u = p_region->p_picture->U_PIXELS;
738 p_dst_v = p_region->p_picture->V_PIXELS;
739 p_dst_a = p_region->p_picture->A_PIXELS;
740 i_pitch = p_region->p_picture->A_PITCH;
742 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
743 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
745 for( y = 0; y < i_line_thickness; y++ )
747 int i_extra = p_this_glyph->bitmap.width;
751 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
752 (p_this_glyph_pos->x + p_this_glyph->left);
754 for( x = 0; x < i_extra; x++ )
758 /* break the underline around the tails of any glyphs which cross it */
759 for( z = x - i_line_thickness;
760 z < x + i_line_thickness && b_ok;
763 if( p_next_glyph && ( z >= i_extra ) )
765 int i_row = i_line_offset + p_next_glyph->top + y;
767 if( ( p_next_glyph->bitmap.rows > i_row ) &&
768 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
773 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
775 int i_row = i_line_offset + p_this_glyph->top + y;
777 if( ( p_this_glyph->bitmap.rows > i_row ) &&
778 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
787 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
788 p_dst_u[i_offset+x] = i_u;
789 p_dst_v[i_offset+x] = i_v;
790 p_dst_a[i_offset+x] = 255;
797 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
799 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
800 int i_pitch = p_region->p_picture->A_PITCH;
803 for( ; p_line != NULL; p_line = p_line->p_next )
805 int i_glyph_tmax=0, i = 0;
806 int i_bitmap_offset, i_offset, i_align_offset = 0;
807 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
809 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
810 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
813 if( p_line->i_width < i_width )
815 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
817 i_align_offset = i_width - p_line->i_width;
819 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
821 i_align_offset = ( i_width - p_line->i_width ) / 2;
825 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
827 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
829 i_offset = ( p_line->p_glyph_pos[ i ].y +
830 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
831 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
832 i_align_offset +xoffset;
834 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
836 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
838 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
839 if( p_dst[i_offset+x] <
840 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
842 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
851 /*****************************************************************************
852 * Render: place string in picture
853 *****************************************************************************
854 * This function merges the previously rendered freetype glyphs into a picture
855 *****************************************************************************/
856 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
857 line_desc_t *p_line, int i_width, int i_height )
859 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
861 int i, x, y, i_pitch, i_alpha;
862 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
864 if( i_width == 0 || i_height == 0 )
867 /* Create a new subpicture region */
868 memset( &fmt, 0, sizeof(video_format_t) );
869 fmt.i_chroma = VLC_CODEC_YUVA;
871 fmt.i_width = fmt.i_visible_width = i_width + 6;
872 fmt.i_height = fmt.i_visible_height = i_height + 6;
873 if( p_region->fmt.i_visible_width > 0 )
874 fmt.i_visible_width = p_region->fmt.i_visible_width;
875 if( p_region->fmt.i_visible_height > 0 )
876 fmt.i_visible_height = p_region->fmt.i_visible_height;
877 fmt.i_x_offset = fmt.i_y_offset = 0;
879 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
880 if( !p_region->p_picture )
884 /* Calculate text color components */
885 YUVFromRGB( (p_line->i_red << 16) |
886 (p_line->i_green << 8) |
889 i_alpha = p_line->i_alpha;
891 p_dst_y = p_region->p_picture->Y_PIXELS;
892 p_dst_u = p_region->p_picture->U_PIXELS;
893 p_dst_v = p_region->p_picture->V_PIXELS;
894 p_dst_a = p_region->p_picture->A_PIXELS;
895 i_pitch = p_region->p_picture->A_PITCH;
897 /* Initialize the region pixels */
898 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
900 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
901 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
902 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
903 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
907 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
908 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
909 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
910 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
912 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
913 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
915 DrawBlack( p_line, i_width, p_region, 0, 0);
916 DrawBlack( p_line, i_width, p_region, -1, 0);
917 DrawBlack( p_line, i_width, p_region, 0, -1);
918 DrawBlack( p_line, i_width, p_region, 1, 0);
919 DrawBlack( p_line, i_width, p_region, 0, 1);
922 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
924 DrawBlack( p_line, i_width, p_region, -1, -1);
925 DrawBlack( p_line, i_width, p_region, -1, 1);
926 DrawBlack( p_line, i_width, p_region, 1, -1);
927 DrawBlack( p_line, i_width, p_region, 1, 1);
929 DrawBlack( p_line, i_width, p_region, -2, 0);
930 DrawBlack( p_line, i_width, p_region, 0, -2);
931 DrawBlack( p_line, i_width, p_region, 2, 0);
932 DrawBlack( p_line, i_width, p_region, 0, 2);
934 DrawBlack( p_line, i_width, p_region, -2, -2);
935 DrawBlack( p_line, i_width, p_region, -2, 2);
936 DrawBlack( p_line, i_width, p_region, 2, -2);
937 DrawBlack( p_line, i_width, p_region, 2, 2);
939 DrawBlack( p_line, i_width, p_region, -3, 0);
940 DrawBlack( p_line, i_width, p_region, 0, -3);
941 DrawBlack( p_line, i_width, p_region, 3, 0);
942 DrawBlack( p_line, i_width, p_region, 0, 3);
945 for( ; p_line != NULL; p_line = p_line->p_next )
947 int i_glyph_tmax = 0;
948 int i_bitmap_offset, i_offset, i_align_offset = 0;
949 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
951 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
952 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
955 if( p_line->i_width < i_width )
957 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
959 i_align_offset = i_width - p_line->i_width;
961 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
963 i_align_offset = ( i_width - p_line->i_width ) / 2;
967 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
969 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
971 i_offset = ( p_line->p_glyph_pos[ i ].y +
972 i_glyph_tmax - p_glyph->top + 3 ) *
973 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
976 if( p_line->b_new_color_mode )
978 /* Every glyph can (and in fact must) have its own color */
979 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
982 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
984 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
986 uint8_t i_y_local = i_y;
987 uint8_t i_u_local = i_u;
988 uint8_t i_v_local = i_v;
990 if( p_line->p_fg_bg_ratio != 0x00 )
992 int i_split = p_glyph->bitmap.width *
993 p_line->p_fg_bg_ratio[ i ] / 0x7f;
997 YUVFromRGB( p_line->p_bg_rgb[ i ],
998 &i_y_local, &i_u_local, &i_v_local );
1002 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1004 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1005 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1007 p_dst_u[i_offset+x] = i_u;
1008 p_dst_v[i_offset+x] = i_v;
1010 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1011 p_dst_a[i_offset+x] = 0xff;
1014 i_offset += i_pitch;
1017 if( p_line->pi_underline_thickness[ i ] )
1019 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1020 p_line->pi_underline_offset[ i ],
1021 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1022 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1023 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1024 i_glyph_tmax, i_align_offset,
1031 /* Apply the alpha setting */
1032 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1033 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1039 * This function renders a text subpicture region into another one.
1040 * It also calculates the size needed for this string, and renders the
1041 * needed glyphs into memory. It is used as pf_add_string callback in
1042 * the vout method by this module
1044 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1045 subpicture_region_t *p_region_in )
1047 filter_sys_t *p_sys = p_filter->p_sys;
1048 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1049 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1050 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1051 int i_string_length;
1053 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1054 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1064 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1065 psz_string = p_region_in->psz_text;
1066 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1068 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1069 i_scale = val.i_int;
1071 if( p_region_in->p_style )
1073 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1074 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1075 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1079 i_font_color = p_sys->i_font_color;
1080 i_font_alpha = 255 - p_sys->i_font_opacity;
1081 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1084 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1085 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1086 SetFontSize( p_filter, i_font_size );
1088 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1089 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1090 i_blue = i_font_color & 0x000000FF;
1092 result.x = result.y = 0;
1093 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1095 psz_unicode = psz_unicode_orig =
1096 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1097 if( psz_unicode == NULL )
1099 #if defined(WORDS_BIGENDIAN)
1100 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1102 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1104 if( iconv_handle == (vlc_iconv_t)-1 )
1106 msg_Warn( p_filter, "unable to do conversion" );
1112 const char *p_in_buffer = psz_string;
1113 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1114 i_in_bytes = strlen( psz_string );
1115 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1116 i_out_bytes_left = i_out_bytes;
1117 p_out_buffer = (char *)psz_unicode;
1118 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1120 &p_out_buffer, &i_out_bytes_left );
1122 vlc_iconv_close( iconv_handle );
1126 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1127 "bytes left %u", (unsigned)i_in_bytes );
1130 *(uint32_t*)p_out_buffer = 0;
1131 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1134 #if defined(HAVE_FRIBIDI)
1136 uint32_t *p_fribidi_string;
1137 int32_t start_pos, pos = 0;
1139 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1140 if( !p_fribidi_string )
1143 /* Do bidi conversion line-by-line */
1144 while( pos < i_string_length )
1146 while( pos < i_string_length )
1148 i_char = psz_unicode[pos];
1149 if (i_char != '\r' && i_char != '\n')
1151 p_fribidi_string[pos] = i_char;
1155 while( pos < i_string_length )
1157 i_char = psz_unicode[pos];
1158 if (i_char == '\r' || i_char == '\n')
1162 if (pos > start_pos)
1164 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1165 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1168 (FriBidiChar*)p_fribidi_string + start_pos,
1173 free( psz_unicode_orig );
1174 psz_unicode = psz_unicode_orig = p_fribidi_string;
1175 p_fribidi_string[ i_string_length ] = 0;
1179 /* Calculate relative glyph positions and a bounding box for the
1181 if( !(p_line = NewLine( strlen( psz_string ))) )
1184 i_pen_x = i_pen_y = 0;
1186 psz_line_start = psz_unicode;
1188 #define face p_sys->p_face
1189 #define glyph face->glyph
1191 while( *psz_unicode )
1193 i_char = *psz_unicode++;
1194 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1199 if( i_char == '\n' )
1201 psz_line_start = psz_unicode;
1202 if( !(p_next = NewLine( strlen( psz_string ))) )
1204 p_line->p_next = p_next;
1205 p_line->i_width = line.xMax;
1206 p_line->i_height = face->size->metrics.height >> 6;
1207 p_line->pp_glyphs[ i ] = NULL;
1208 p_line->i_alpha = i_font_alpha;
1209 p_line->i_red = i_red;
1210 p_line->i_green = i_green;
1211 p_line->i_blue = i_blue;
1214 result.x = __MAX( result.x, line.xMax );
1215 result.y += face->size->metrics.height >> 6;
1218 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1219 i_pen_y += face->size->metrics.height >> 6;
1221 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1226 i_glyph_index = FT_Get_Char_Index( face, i_char );
1227 if( p_sys->i_use_kerning && i_glyph_index
1231 FT_Get_Kerning( face, i_previous, i_glyph_index,
1232 ft_kerning_default, &delta );
1233 i_pen_x += delta.x >> 6;
1236 p_line->p_glyph_pos[ i ].x = i_pen_x;
1237 p_line->p_glyph_pos[ i ].y = i_pen_y;
1238 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1241 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1245 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1248 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1252 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1253 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1256 FT_Done_Glyph( tmp_glyph );
1259 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1262 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1263 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1264 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1266 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1267 p_line->pp_glyphs[ i ] = NULL;
1269 p_line = NewLine( strlen( psz_string ));
1270 if( p_prev ) p_prev->p_next = p_line;
1271 else p_lines = p_line;
1273 uint32_t *psz_unicode_saved = psz_unicode;
1274 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1278 if( psz_unicode == psz_line_start )
1279 { /* try harder to break that line */
1280 psz_unicode = psz_unicode_saved;
1281 while( psz_unicode > psz_line_start &&
1282 *psz_unicode != '_' && *psz_unicode != '/' &&
1283 *psz_unicode != '\\' && *psz_unicode != '.' )
1288 if( psz_unicode == psz_line_start )
1290 msg_Warn( p_filter, "unbreakable string" );
1295 *psz_unicode = '\n';
1297 psz_unicode = psz_line_start;
1300 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1303 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1304 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1306 i_previous = i_glyph_index;
1307 i_pen_x += glyph->advance.x >> 6;
1311 p_line->i_width = line.xMax;
1312 p_line->i_height = face->size->metrics.height >> 6;
1313 p_line->pp_glyphs[ i ] = NULL;
1314 p_line->i_alpha = i_font_alpha;
1315 p_line->i_red = i_red;
1316 p_line->i_green = i_green;
1317 p_line->i_blue = i_blue;
1318 result.x = __MAX( result.x, line.xMax );
1319 result.y += line.yMax - line.yMin;
1324 p_region_out->i_x = p_region_in->i_x;
1325 p_region_out->i_y = p_region_in->i_y;
1327 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1328 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1330 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1332 free( psz_unicode_orig );
1333 FreeLines( p_lines );
1337 free( psz_unicode_orig );
1338 FreeLines( p_lines );
1339 return VLC_EGENERIC;
1342 #ifdef HAVE_FONTCONFIG
1343 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1344 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1345 bool b_italic, bool b_uline )
1347 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1351 p_style->i_font_size = i_font_size;
1352 p_style->i_font_color = i_font_color;
1353 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1354 p_style->b_italic = b_italic;
1355 p_style->b_bold = b_bold;
1356 p_style->b_underline = b_uline;
1358 p_style->psz_fontname = strdup( psz_fontname );
1363 static void DeleteStyle( ft_style_t *p_style )
1367 free( p_style->psz_fontname );
1372 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1379 if(( s1->i_font_size == s2->i_font_size ) &&
1380 ( s1->i_font_color == s2->i_font_color ) &&
1381 ( s1->b_italic == s2->b_italic ) &&
1382 ( s1->b_bold == s2->b_bold ) &&
1383 ( s1->b_underline == s2->b_underline ) &&
1384 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1391 static void IconvText( filter_t *p_filter, const char *psz_string,
1392 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1394 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1396 /* If memory hasn't been allocated for our output string, allocate it here
1397 * - the calling function must now be responsible for freeing it.
1399 if( !*ppsz_unicode )
1400 *ppsz_unicode = (uint32_t *)
1401 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1403 /* We don't need to handle a NULL pointer in *ppsz_unicode
1404 * if we are instead testing for a non NULL value like we are here */
1408 #if defined(WORDS_BIGENDIAN)
1409 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1411 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1413 if( iconv_handle != (vlc_iconv_t)-1 )
1415 char *p_in_buffer, *p_out_buffer;
1416 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1417 i_in_bytes = strlen( psz_string );
1418 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1419 i_out_bytes_left = i_out_bytes;
1420 p_in_buffer = (char *) psz_string;
1421 p_out_buffer = (char *) *ppsz_unicode;
1422 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1423 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1425 vlc_iconv_close( iconv_handle );
1429 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1430 "bytes left %u", (unsigned)i_in_bytes );
1434 *(uint32_t*)p_out_buffer = 0;
1436 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1441 msg_Warn( p_filter, "unable to do conversion" );
1446 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1447 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1450 ft_style_t *p_style = NULL;
1452 char *psz_fontname = NULL;
1453 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1454 uint32_t i_karaoke_bg_color = i_font_color;
1455 int i_font_size = p_sys->i_font_size;
1457 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1458 &i_font_color, &i_karaoke_bg_color ))
1460 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1461 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1466 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1467 bool b_uline, int i_karaoke_bgcolor,
1468 line_desc_t *p_line, uint32_t *psz_unicode,
1469 int *pi_pen_x, int i_pen_y, int *pi_start,
1470 FT_Vector *p_result )
1475 bool b_first_on_line = true;
1478 int i_pen_x_start = *pi_pen_x;
1480 uint32_t *psz_unicode_start = psz_unicode;
1482 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1484 /* Account for part of line already in position */
1485 for( i=0; i<*pi_start; i++ )
1489 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1490 ft_glyph_bbox_pixels, &glyph_size );
1492 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1493 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1494 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1495 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1501 b_first_on_line = false;
1503 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1509 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1510 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1514 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1515 ft_kerning_default, &delta );
1516 *pi_pen_x += delta.x >> 6;
1518 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1519 p_line->p_glyph_pos[ i ].y = i_pen_y;
1521 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1525 "unable to render text FT_Load_Glyph returned %d", i_error );
1526 p_line->pp_glyphs[ i ] = NULL;
1527 return VLC_EGENERIC;
1529 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1533 "unable to render text FT_Get_Glyph returned %d", i_error );
1534 p_line->pp_glyphs[ i ] = NULL;
1535 return VLC_EGENERIC;
1537 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1538 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1541 FT_Done_Glyph( tmp_glyph );
1546 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1547 p_face->size->metrics.y_scale));
1548 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1549 p_face->size->metrics.y_scale));
1551 p_line->pi_underline_offset[ i ] =
1552 ( aOffset < 0 ) ? -aOffset : aOffset;
1553 p_line->pi_underline_thickness[ i ] =
1554 ( aSize < 0 ) ? -aSize : aSize;
1556 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1557 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1558 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1559 p_line->p_fg_bg_ratio[ i ] = 0x00;
1561 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1562 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1563 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1565 for( ; i >= *pi_start; i-- )
1566 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1569 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1573 if( psz_unicode == psz_unicode_start )
1575 if( b_first_on_line )
1577 msg_Warn( p_filter, "unbreakable string" );
1578 p_line->pp_glyphs[ i ] = NULL;
1579 return VLC_EGENERIC;
1581 *pi_pen_x = i_pen_x_start;
1583 p_line->i_width = line.xMax;
1584 p_line->i_height = __MAX( p_line->i_height,
1585 p_face->size->metrics.height >> 6 );
1586 p_line->pp_glyphs[ i ] = NULL;
1588 p_result->x = __MAX( p_result->x, line.xMax );
1589 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1590 i_yMax - i_yMin ) );
1595 *psz_unicode = '\n';
1597 psz_unicode = psz_unicode_start;
1598 *pi_pen_x = i_pen_x_start;
1606 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1607 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1609 i_previous = i_glyph_index;
1610 *pi_pen_x += p_face->glyph->advance.x >> 6;
1613 p_line->i_width = line.xMax;
1614 p_line->i_height = __MAX( p_line->i_height,
1615 p_face->size->metrics.height >> 6 );
1616 p_line->pp_glyphs[ i ] = NULL;
1618 p_result->x = __MAX( p_result->x, line.xMax );
1619 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1620 line.yMax - line.yMin ) );
1624 /* Get rid of any text processed - if necessary repositioning
1625 * at the start of a new line of text
1629 *psz_unicode_start = '\0';
1631 else if( psz_unicode > psz_unicode_start )
1633 for( i=0; psz_unicode[ i ]; i++ )
1634 psz_unicode_start[ i ] = psz_unicode[ i ];
1635 psz_unicode_start[ i ] = '\0';
1641 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1642 uint32_t **psz_text_out, uint32_t *pi_runs,
1643 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1644 ft_style_t *p_style )
1646 uint32_t i_string_length = 0;
1648 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1649 *psz_text_out += i_string_length;
1651 if( ppp_styles && ppi_run_lengths )
1657 *ppp_styles = (ft_style_t **)
1658 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1660 else if( *pi_runs == 1 )
1662 *ppp_styles = (ft_style_t **)
1663 malloc( *pi_runs * sizeof( ft_style_t * ) );
1666 /* We have just malloc'ed this memory successfully -
1667 * *pi_runs HAS to be within the memory area of *ppp_styles */
1670 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1674 if( *ppi_run_lengths )
1676 *ppi_run_lengths = (uint32_t *)
1677 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1679 else if( *pi_runs == 1 )
1681 *ppi_run_lengths = (uint32_t *)
1682 malloc( *pi_runs * sizeof( uint32_t ) );
1685 /* same remarks here */
1686 if( *ppi_run_lengths )
1688 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1691 /* If we couldn't use the p_style argument due to memory allocation
1692 * problems above, release it here.
1694 if( p_style ) DeleteStyle( p_style );
1697 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1701 for( k=0; k < p_sys->i_font_attachments; k++ )
1703 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1705 FT_Face p_face = NULL;
1707 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1715 bool match = !strcasecmp( p_face->family_name,
1716 p_style->psz_fontname );
1718 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1719 match = match && p_style->b_bold;
1721 match = match && !p_style->b_bold;
1723 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1724 match = match && p_style->b_italic;
1726 match = match && !p_style->b_italic;
1734 FT_Done_Face( p_face );
1739 return VLC_EGENERIC;
1742 static int ProcessLines( filter_t *p_filter,
1747 uint32_t *pi_run_lengths,
1748 ft_style_t **pp_styles,
1749 line_desc_t **pp_lines,
1751 FT_Vector *p_result,
1755 uint32_t *pi_k_run_lengths,
1756 uint32_t *pi_k_durations )
1758 filter_sys_t *p_sys = p_filter->p_sys;
1759 ft_style_t **pp_char_styles;
1760 int *p_new_positions = NULL;
1761 int8_t *p_levels = NULL;
1762 uint8_t *pi_karaoke_bar = NULL;
1766 /* Assign each character in the text string its style explicitly, so that
1767 * after the characters have been shuffled around by Fribidi, we can re-apply
1768 * the styles, and to simplify the calculation of runs within a line.
1770 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1771 if( !pp_char_styles )
1776 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1777 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1778 * we just won't be able to display the progress bar; at least we'll
1784 for( j = 0; j < i_runs; j++ )
1785 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1786 pp_char_styles[ i++ ] = pp_styles[ j ];
1788 #if defined(HAVE_FRIBIDI)
1790 ft_style_t **pp_char_styles_new;
1791 int *p_old_positions;
1792 uint32_t *p_fribidi_string;
1793 int start_pos, pos = 0;
1795 pp_char_styles_new = (ft_style_t **)
1796 malloc( i_len * sizeof( ft_style_t * ));
1798 p_fribidi_string = (uint32_t *)
1799 malloc( (i_len + 1) * sizeof(uint32_t) );
1800 p_old_positions = (int *)
1801 malloc( (i_len + 1) * sizeof( int ) );
1802 p_new_positions = (int *)
1803 malloc( (i_len + 1) * sizeof( int ) );
1804 p_levels = (int8_t *)
1805 malloc( (i_len + 1) * sizeof( int8_t ) );
1807 if( ! pp_char_styles_new ||
1808 ! p_fribidi_string ||
1809 ! p_old_positions ||
1810 ! p_new_positions ||
1814 free( p_old_positions );
1815 free( p_new_positions );
1816 free( p_fribidi_string );
1817 free( pp_char_styles_new );
1818 free( pi_karaoke_bar );
1820 free( pp_char_styles );
1824 /* Do bidi conversion line-by-line */
1827 while(pos < i_len) {
1828 if (psz_text[pos] != '\n')
1830 p_fribidi_string[pos] = psz_text[pos];
1831 pp_char_styles_new[pos] = pp_char_styles[pos];
1832 p_new_positions[pos] = pos;
1837 while(pos < i_len) {
1838 if (psz_text[pos] == '\n')
1842 if (pos > start_pos)
1844 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1845 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1846 pos - start_pos, &base_dir,
1847 (FriBidiChar*)p_fribidi_string + start_pos,
1848 p_new_positions + start_pos,
1850 p_levels + start_pos );
1851 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1853 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1854 p_old_positions[ j - start_pos ] ];
1855 p_new_positions[ j ] += start_pos;
1859 free( p_old_positions );
1860 free( pp_char_styles );
1861 pp_char_styles = pp_char_styles_new;
1862 psz_text = p_fribidi_string;
1863 p_fribidi_string[ i_len ] = 0;
1866 /* Work out the karaoke */
1867 if( pi_karaoke_bar )
1869 int64_t i_last_duration = 0;
1870 int64_t i_duration = 0;
1871 int64_t i_start_pos = 0;
1872 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1874 for( k = 0; k< i_k_runs; k++ )
1876 double fraction = 0.0;
1878 i_duration += pi_k_durations[ k ];
1880 if( i_duration < i_elapsed )
1882 /* Completely finished this run-length -
1883 * let it render normally */
1887 else if( i_elapsed < i_last_duration )
1889 /* Haven't got up to this segment yet -
1890 * render it completely in karaoke BG mode */
1896 /* Partway through this run */
1898 fraction = (double)(i_elapsed - i_last_duration) /
1899 (double)pi_k_durations[ k ];
1901 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1903 double shade = pi_k_run_lengths[ k ] * fraction;
1905 if( p_new_positions )
1906 j = p_new_positions[ i_start_pos + i ];
1908 j = i_start_pos + i;
1910 if( i < (uint32_t)shade )
1911 pi_karaoke_bar[ j ] = 0xff;
1912 else if( (double)i > shade )
1913 pi_karaoke_bar[ j ] = 0x00;
1916 shade -= (int)shade;
1917 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1918 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1922 i_last_duration = i_duration;
1923 i_start_pos += pi_k_run_lengths[ k ];
1927 free( p_new_positions );
1929 FT_Vector tmp_result;
1931 line_desc_t *p_line = NULL;
1932 line_desc_t *p_prev = NULL;
1938 p_result->x = p_result->y = 0;
1939 tmp_result.x = tmp_result.y = 0;
1942 for( k = 0; k <= (uint32_t) i_len; k++ )
1944 if( ( k == (uint32_t) i_len ) ||
1946 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1948 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1950 /* End of the current style run */
1951 FT_Face p_face = NULL;
1954 /* Look for a match amongst our attachments first */
1955 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1959 char *psz_fontfile = NULL;
1961 psz_fontfile = FontConfig_Select( NULL,
1962 p_style->psz_fontname,
1966 if( psz_fontfile && ! *psz_fontfile )
1968 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1969 " so using default font", p_style->psz_fontname,
1970 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1971 (p_style->b_bold ? "(Bold)" :
1972 (p_style->b_italic ? "(Italic)" : ""))) );
1973 free( psz_fontfile );
1974 psz_fontfile = NULL;
1979 if( FT_New_Face( p_sys->p_library,
1980 psz_fontfile, i_idx, &p_face ) )
1982 free( psz_fontfile );
1983 free( pp_char_styles );
1984 #if defined(HAVE_FRIBIDI)
1987 free( pi_karaoke_bar );
1988 return VLC_EGENERIC;
1990 free( psz_fontfile );
1994 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1996 /* We've loaded a font face which is unhelpful for actually
1997 * rendering text - fallback to the default one.
1999 FT_Done_Face( p_face );
2003 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2004 ft_encoding_unicode ) ||
2005 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2006 p_style->i_font_size ) )
2008 if( p_face ) FT_Done_Face( p_face );
2009 free( pp_char_styles );
2010 #if defined(HAVE_FRIBIDI)
2013 free( pi_karaoke_bar );
2014 return VLC_EGENERIC;
2016 p_sys->i_use_kerning =
2017 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2020 uint32_t *psz_unicode = (uint32_t *)
2021 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2024 if( p_face ) FT_Done_Face( p_face );
2025 free( pp_char_styles );
2026 free( psz_unicode );
2027 #if defined(HAVE_FRIBIDI)
2030 free( pi_karaoke_bar );
2033 memcpy( psz_unicode, psz_text + i_prev,
2034 sizeof( uint32_t ) * ( k - i_prev ) );
2035 psz_unicode[ k - i_prev ] = 0;
2036 while( *psz_unicode )
2040 if( !(p_line = NewLine( i_len - i_prev)) )
2042 if( p_face ) FT_Done_Face( p_face );
2043 free( pp_char_styles );
2044 free( psz_unicode );
2045 #if defined(HAVE_FRIBIDI)
2048 free( pi_karaoke_bar );
2051 /* New Color mode only works in YUVA rendering mode --
2052 * (RGB mode has palette constraints on it). We therefore
2053 * need to populate the legacy colour fields also.
2055 p_line->b_new_color_mode = true;
2056 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2057 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2058 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2059 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2060 p_line->p_next = NULL;
2062 i_pen_y += tmp_result.y;
2066 if( p_prev ) p_prev->p_next = p_line;
2067 else *pp_lines = p_line;
2070 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2071 p_style->i_font_color, p_style->b_underline,
2072 p_style->i_karaoke_bg_color,
2073 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2074 &tmp_result ) != VLC_SUCCESS )
2076 if( p_face ) FT_Done_Face( p_face );
2077 free( pp_char_styles );
2078 free( psz_unicode );
2079 #if defined(HAVE_FRIBIDI)
2082 free( pi_karaoke_bar );
2083 return VLC_EGENERIC;
2088 p_result->x = __MAX( p_result->x, tmp_result.x );
2089 p_result->y += tmp_result.y;
2094 if( *psz_unicode == '\n')
2098 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2100 *c_ptr = *(c_ptr+1);
2105 free( psz_unicode );
2106 if( p_face ) FT_Done_Face( p_face );
2110 free( pp_char_styles );
2111 #if defined(HAVE_FRIBIDI)
2116 p_result->x = __MAX( p_result->x, tmp_result.x );
2117 p_result->y += tmp_result.y;
2120 if( pi_karaoke_bar )
2123 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2125 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2127 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2131 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2133 /* 100% BG colour will render faster if we
2134 * instead make it 100% FG colour, so leave
2135 * the ratio alone and copy the value across
2137 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2141 if( pi_karaoke_bar[ i ] & 0x80 )
2143 /* Swap Left and Right sides over for Right aligned
2144 * language text (eg. Arabic, Hebrew)
2146 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2148 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2149 p_line->p_bg_rgb[ k ] = i_tmp;
2151 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2154 /* Jump over the '\n' at the line-end */
2157 free( pi_karaoke_bar );
2163 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2164 subpicture_region_t *p_region_in )
2166 int rv = VLC_SUCCESS;
2167 stream_t *p_sub = NULL;
2168 xml_reader_t *p_xml_reader = NULL;
2170 if( !p_region_in || !p_region_in->psz_html )
2171 return VLC_EGENERIC;
2173 /* Reset the default fontsize in case screen metrics have changed */
2174 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2176 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2177 (uint8_t *) p_region_in->psz_html,
2178 strlen( p_region_in->psz_html ),
2182 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2183 if( p_filter->p_sys->p_xml )
2185 bool b_karaoke = false;
2187 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2190 /* Look for Root Node */
2191 if( xml_ReaderRead( p_xml_reader ) == 1 )
2193 char *psz_node = xml_ReaderName( p_xml_reader );
2195 if( !strcasecmp( "karaoke", psz_node ) )
2197 /* We're going to have to render the text a number
2198 * of times to show the progress marker on the text.
2200 var_SetBool( p_filter, "text-rerender", true );
2203 else if( !strcasecmp( "text", psz_node ) )
2209 /* Only text and karaoke tags are supported */
2210 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2211 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2212 p_xml_reader = NULL;
2224 uint32_t i_runs = 0;
2225 uint32_t i_k_runs = 0;
2226 uint32_t *pi_run_lengths = NULL;
2227 uint32_t *pi_k_run_lengths = NULL;
2228 uint32_t *pi_k_durations = NULL;
2229 ft_style_t **pp_styles = NULL;
2231 line_desc_t *p_lines = NULL;
2233 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2234 sizeof( uint32_t ) );
2239 rv = ProcessNodes( p_filter, p_xml_reader,
2240 p_region_in->p_style, psz_text, &i_len,
2241 &i_runs, &pi_run_lengths, &pp_styles,
2243 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2246 p_region_out->i_x = p_region_in->i_x;
2247 p_region_out->i_y = p_region_in->i_y;
2249 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2251 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2252 pi_run_lengths, pp_styles, &p_lines, &result,
2253 b_karaoke, i_k_runs, pi_k_run_lengths,
2257 for( k=0; k<i_runs; k++)
2258 DeleteStyle( pp_styles[k] );
2260 free( pi_run_lengths );
2263 /* Don't attempt to render text that couldn't be layed out
2266 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2268 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2270 Render( p_filter, p_region_out, p_lines,
2271 result.x, result.y );
2275 RenderYUVA( p_filter, p_region_out, p_lines,
2276 result.x, result.y );
2280 FreeLines( p_lines );
2282 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2285 stream_Delete( p_sub );
2291 static char* FontConfig_Select( FcConfig* priv, const char* family,
2292 bool b_bold, bool b_italic, int *i_idx )
2295 FcPattern *pat, *p_pat;
2299 pat = FcPatternCreate();
2300 if (!pat) return NULL;
2302 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2303 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2304 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2305 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2307 FcDefaultSubstitute( pat );
2309 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2311 FcPatternDestroy( pat );
2315 p_pat = FcFontMatch( priv, pat, &result );
2316 FcPatternDestroy( pat );
2317 if( !p_pat ) return NULL;
2319 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2320 || ( val_b != FcTrue ) )
2322 FcPatternDestroy( p_pat );
2325 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2330 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2332 FcPatternDestroy( p_pat );
2337 if( strcasecmp((const char*)val_s, family ) != 0 )
2338 msg_Warn( p_filter, "fontconfig: selected font family is not"
2339 "the requested one: '%s' != '%s'\n",
2340 (const char*)val_s, family );
2343 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2345 FcPatternDestroy( p_pat );
2349 FcPatternDestroy( p_pat );
2350 return strdup( (const char*)val_s );
2354 static void FreeLine( line_desc_t *p_line )
2357 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2359 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2361 free( p_line->pp_glyphs );
2362 free( p_line->p_glyph_pos );
2363 free( p_line->p_fg_rgb );
2364 free( p_line->p_bg_rgb );
2365 free( p_line->p_fg_bg_ratio );
2366 free( p_line->pi_underline_offset );
2367 free( p_line->pi_underline_thickness );
2371 static void FreeLines( line_desc_t *p_lines )
2373 line_desc_t *p_line, *p_next;
2375 if( !p_lines ) return;
2377 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2379 p_next = p_line->p_next;
2384 static line_desc_t *NewLine( int i_count )
2386 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2388 if( !p_line ) return NULL;
2389 p_line->i_height = 0;
2390 p_line->i_width = 0;
2391 p_line->p_next = NULL;
2393 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2394 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2395 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2396 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2397 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2398 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2399 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2400 if( ( p_line->pp_glyphs == NULL ) ||
2401 ( p_line->p_glyph_pos == NULL ) ||
2402 ( p_line->p_fg_rgb == NULL ) ||
2403 ( p_line->p_bg_rgb == NULL ) ||
2404 ( p_line->p_fg_bg_ratio == NULL ) ||
2405 ( p_line->pi_underline_offset == NULL ) ||
2406 ( p_line->pi_underline_thickness == NULL ) )
2408 free( p_line->pi_underline_thickness );
2409 free( p_line->pi_underline_offset );
2410 free( p_line->p_fg_rgb );
2411 free( p_line->p_bg_rgb );
2412 free( p_line->p_fg_bg_ratio );
2413 free( p_line->p_glyph_pos );
2414 free( p_line->pp_glyphs );
2418 p_line->pp_glyphs[0] = NULL;
2419 p_line->b_new_color_mode = false;
2424 static int GetFontSize( filter_t *p_filter )
2426 filter_sys_t *p_sys = p_filter->p_sys;
2430 if( p_sys->i_default_font_size )
2432 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2433 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2435 i_size = p_sys->i_default_font_size;
2439 var_Get( p_filter, "freetype-rel-fontsize", &val );
2442 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2443 p_filter->p_sys->i_display_height =
2444 p_filter->fmt_out.video.i_height;
2449 msg_Warn( p_filter, "invalid fontsize, using 12" );
2450 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2451 i_size = 12 * val.i_int / 1000;
2458 static int SetFontSize( filter_t *p_filter, int i_size )
2460 filter_sys_t *p_sys = p_filter->p_sys;
2464 i_size = GetFontSize( p_filter );
2466 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2469 p_sys->i_font_size = i_size;
2471 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2473 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2474 return VLC_EGENERIC;
2480 static void YUVFromRGB( uint32_t i_argb,
2481 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2483 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2484 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2485 int i_blue = ( i_argb & 0x000000ff );
2487 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2488 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2489 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2490 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2491 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2492 -585 * i_blue + 4096 + 1048576) >> 13, 240);