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();
385 msg_Err( p_filter, "Creating fontpattern failed");
391 dialog_ProgressSet( p_dialog, NULL, 0.7 );
393 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
394 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
395 free( psz_fontsize );
397 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
399 msg_Err( p_filter, "FontSubstitute failed");
402 FcDefaultSubstitute( fontpattern );
406 dialog_ProgressSet( p_dialog, NULL, 0.8 );
408 /* testing fontresult here doesn't do any good really, but maybe it will
409 * in future as fontconfig code doesn't set it in all cases and just
410 * returns NULL or doesn't set to to Match on all Match cases.*/
411 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
412 if( !fontmatch || fontresult == FcResultNoMatch )
414 msg_Err( p_filter, "Fontmatching failed");
418 FcPatternGetString( fontmatch, FC_FILE, 0, (FcChar8 **)&psz_fontfile);
419 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
422 msg_Err( p_filter, "Failed to get fontfile");
426 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
427 p_sys->psz_fontfamily = strdup( psz_fontfamily );
431 dialog_ProgressSet( p_dialog, NULL, 1.0 );
432 dialog_ProgressDestroy( p_dialog );
437 p_sys->psz_fontfamily = strdup( DEFAULT_FONT )
438 psz_fontfile = psz_fontfamily;
441 i_error = FT_Init_FreeType( &p_sys->p_library );
444 msg_Err( p_filter, "couldn't initialize freetype" );
448 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
449 fontindex, &p_sys->p_face );
451 if( i_error == FT_Err_Unknown_File_Format )
453 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
458 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
462 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
465 msg_Err( p_filter, "font has no unicode translation table" );
469 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
471 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
474 p_sys->pp_font_attachments = NULL;
475 p_sys->i_font_attachments = 0;
477 p_filter->pf_render_text = RenderText;
478 #ifdef HAVE_FONTCONFIG
479 p_filter->pf_render_html = RenderHtml;
480 FcPatternDestroy( fontmatch );
481 FcPatternDestroy( fontpattern );
483 p_filter->pf_render_html = NULL;
486 free( psz_fontfamily );
487 LoadFontsFromAttachments( p_filter );
492 #ifdef HAVE_FONTCONFIG
493 if( fontmatch ) FcPatternDestroy( fontmatch );
494 if( fontpattern ) FcPatternDestroy( fontpattern );
496 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
497 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
498 free( psz_fontfamily );
503 /*****************************************************************************
504 * Destroy: destroy Clone video thread output method
505 *****************************************************************************
506 * Clean up all data and library connections
507 *****************************************************************************/
508 static void Destroy( vlc_object_t *p_this )
510 filter_t *p_filter = (filter_t *)p_this;
511 filter_sys_t *p_sys = p_filter->p_sys;
513 if( p_sys->pp_font_attachments )
517 for( k = 0; k < p_sys->i_font_attachments; k++ )
518 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
520 free( p_sys->pp_font_attachments );
523 #ifdef HAVE_FONTCONFIG
524 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
525 free( p_sys->psz_fontfamily );
528 /* FcFini asserts calling the subfunction FcCacheFini()
529 * even if no other library functions have been made since FcInit(),
530 * so don't call it. */
532 FT_Done_Face( p_sys->p_face );
533 FT_Done_FreeType( p_sys->p_library );
537 /*****************************************************************************
538 * Make any TTF/OTF fonts present in the attachments of the media file
539 * and store them for later use by the FreeType Engine
540 *****************************************************************************/
541 static int LoadFontsFromAttachments( filter_t *p_filter )
543 filter_sys_t *p_sys = p_filter->p_sys;
544 input_thread_t *p_input;
545 input_attachment_t **pp_attachments;
546 int i_attachments_cnt;
548 int rv = VLC_SUCCESS;
550 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
554 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
556 vlc_object_release(p_input);
560 p_sys->i_font_attachments = 0;
561 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
562 if(! p_sys->pp_font_attachments )
565 for( k = 0; k < i_attachments_cnt; k++ )
567 input_attachment_t *p_attach = pp_attachments[k];
569 if( p_sys->pp_font_attachments )
571 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
572 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
573 ( p_attach->i_data > 0 ) &&
574 ( p_attach->p_data != NULL ) )
576 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
580 vlc_input_attachment_Delete( p_attach );
585 vlc_input_attachment_Delete( p_attach );
588 free( pp_attachments );
590 vlc_object_release(p_input);
595 /*****************************************************************************
596 * Render: place string in picture
597 *****************************************************************************
598 * This function merges the previously rendered freetype glyphs into a picture
599 *****************************************************************************/
600 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
601 line_desc_t *p_line, int i_width, int i_height )
603 static const uint8_t pi_gamma[16] =
604 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
605 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
609 int i, x, y, i_pitch;
610 uint8_t i_y; /* YUV values, derived from incoming RGB */
613 /* Create a new subpicture region */
614 memset( &fmt, 0, sizeof(video_format_t) );
615 fmt.i_chroma = VLC_CODEC_YUVP;
617 fmt.i_width = fmt.i_visible_width = i_width + 4;
618 fmt.i_height = fmt.i_visible_height = i_height + 4;
619 if( p_region->fmt.i_visible_width > 0 )
620 fmt.i_visible_width = p_region->fmt.i_visible_width;
621 if( p_region->fmt.i_visible_height > 0 )
622 fmt.i_visible_height = p_region->fmt.i_visible_height;
623 fmt.i_x_offset = fmt.i_y_offset = 0;
625 assert( !p_region->p_picture );
626 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
627 if( !p_region->p_picture )
631 /* Calculate text color components */
632 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
633 25 * p_line->i_blue + 128) >> 8) + 16;
634 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
635 112 * p_line->i_blue + 128) >> 8) + 128;
636 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
637 18 * p_line->i_blue + 128) >> 8) + 128;
640 fmt.p_palette->i_entries = 16;
641 for( i = 0; i < 8; i++ )
643 fmt.p_palette->palette[i][0] = 0;
644 fmt.p_palette->palette[i][1] = 0x80;
645 fmt.p_palette->palette[i][2] = 0x80;
646 fmt.p_palette->palette[i][3] = pi_gamma[i];
647 fmt.p_palette->palette[i][3] =
648 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
650 for( i = 8; i < fmt.p_palette->i_entries; i++ )
652 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
653 fmt.p_palette->palette[i][1] = i_u;
654 fmt.p_palette->palette[i][2] = i_v;
655 fmt.p_palette->palette[i][3] = pi_gamma[i];
656 fmt.p_palette->palette[i][3] =
657 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
660 p_dst = p_region->p_picture->Y_PIXELS;
661 i_pitch = p_region->p_picture->Y_PITCH;
663 /* Initialize the region pixels */
664 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
666 for( ; p_line != NULL; p_line = p_line->p_next )
668 int i_glyph_tmax = 0;
669 int i_bitmap_offset, i_offset, i_align_offset = 0;
670 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
672 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
673 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
676 if( p_line->i_width < i_width )
678 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
680 i_align_offset = i_width - p_line->i_width;
682 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
684 i_align_offset = ( i_width - p_line->i_width ) / 2;
688 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
690 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
692 i_offset = ( p_line->p_glyph_pos[ i ].y +
693 i_glyph_tmax - p_glyph->top + 2 ) *
694 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
697 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
699 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
701 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
703 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
710 /* Outlining (find something better than nearest neighbour filtering ?) */
713 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
714 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
715 uint8_t left, current;
717 for( y = 1; y < (int)fmt.i_height - 1; y++ )
719 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
720 p_dst += p_region->p_picture->Y_PITCH;
723 for( x = 1; x < (int)fmt.i_width - 1; x++ )
726 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
727 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;
731 memset( p_top, 0, fmt.i_width );
737 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
738 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
739 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
740 int i_glyph_tmax, int i_align_offset,
741 uint8_t i_y, uint8_t i_u, uint8_t i_v,
742 subpicture_region_t *p_region)
746 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
748 p_dst_y = p_region->p_picture->Y_PIXELS;
749 p_dst_u = p_region->p_picture->U_PIXELS;
750 p_dst_v = p_region->p_picture->V_PIXELS;
751 p_dst_a = p_region->p_picture->A_PIXELS;
752 i_pitch = p_region->p_picture->A_PITCH;
754 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
755 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
757 for( y = 0; y < i_line_thickness; y++ )
759 int i_extra = p_this_glyph->bitmap.width;
763 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
764 (p_this_glyph_pos->x + p_this_glyph->left);
766 for( x = 0; x < i_extra; x++ )
770 /* break the underline around the tails of any glyphs which cross it */
771 for( z = x - i_line_thickness;
772 z < x + i_line_thickness && b_ok;
775 if( p_next_glyph && ( z >= i_extra ) )
777 int i_row = i_line_offset + p_next_glyph->top + y;
779 if( ( p_next_glyph->bitmap.rows > i_row ) &&
780 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
785 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
787 int i_row = i_line_offset + p_this_glyph->top + y;
789 if( ( p_this_glyph->bitmap.rows > i_row ) &&
790 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
799 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
800 p_dst_u[i_offset+x] = i_u;
801 p_dst_v[i_offset+x] = i_v;
802 p_dst_a[i_offset+x] = 255;
809 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
811 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
812 int i_pitch = p_region->p_picture->A_PITCH;
815 for( ; p_line != NULL; p_line = p_line->p_next )
817 int i_glyph_tmax=0, i = 0;
818 int i_bitmap_offset, i_offset, i_align_offset = 0;
819 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
821 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
822 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
825 if( p_line->i_width < i_width )
827 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
829 i_align_offset = i_width - p_line->i_width;
831 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
833 i_align_offset = ( i_width - p_line->i_width ) / 2;
837 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
839 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
841 i_offset = ( p_line->p_glyph_pos[ i ].y +
842 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
843 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
844 i_align_offset +xoffset;
846 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
848 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
850 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
851 if( p_dst[i_offset+x] <
852 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
854 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
863 /*****************************************************************************
864 * Render: place string in picture
865 *****************************************************************************
866 * This function merges the previously rendered freetype glyphs into a picture
867 *****************************************************************************/
868 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
869 line_desc_t *p_line, int i_width, int i_height )
871 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
873 int i, x, y, i_pitch, i_alpha;
874 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
876 if( i_width == 0 || i_height == 0 )
879 /* Create a new subpicture region */
880 memset( &fmt, 0, sizeof(video_format_t) );
881 fmt.i_chroma = VLC_CODEC_YUVA;
883 fmt.i_width = fmt.i_visible_width = i_width + 6;
884 fmt.i_height = fmt.i_visible_height = i_height + 6;
885 if( p_region->fmt.i_visible_width > 0 )
886 fmt.i_visible_width = p_region->fmt.i_visible_width;
887 if( p_region->fmt.i_visible_height > 0 )
888 fmt.i_visible_height = p_region->fmt.i_visible_height;
889 fmt.i_x_offset = fmt.i_y_offset = 0;
891 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
892 if( !p_region->p_picture )
896 /* Calculate text color components */
897 YUVFromRGB( (p_line->i_red << 16) |
898 (p_line->i_green << 8) |
901 i_alpha = p_line->i_alpha;
903 p_dst_y = p_region->p_picture->Y_PIXELS;
904 p_dst_u = p_region->p_picture->U_PIXELS;
905 p_dst_v = p_region->p_picture->V_PIXELS;
906 p_dst_a = p_region->p_picture->A_PIXELS;
907 i_pitch = p_region->p_picture->A_PITCH;
909 /* Initialize the region pixels */
910 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
912 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
913 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
914 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
915 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
919 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
920 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
921 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
922 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
924 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
925 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
927 DrawBlack( p_line, i_width, p_region, 0, 0);
928 DrawBlack( p_line, i_width, p_region, -1, 0);
929 DrawBlack( p_line, i_width, p_region, 0, -1);
930 DrawBlack( p_line, i_width, p_region, 1, 0);
931 DrawBlack( p_line, i_width, p_region, 0, 1);
934 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
936 DrawBlack( p_line, i_width, p_region, -1, -1);
937 DrawBlack( p_line, i_width, p_region, -1, 1);
938 DrawBlack( p_line, i_width, p_region, 1, -1);
939 DrawBlack( p_line, i_width, p_region, 1, 1);
941 DrawBlack( p_line, i_width, p_region, -2, 0);
942 DrawBlack( p_line, i_width, p_region, 0, -2);
943 DrawBlack( p_line, i_width, p_region, 2, 0);
944 DrawBlack( p_line, i_width, p_region, 0, 2);
946 DrawBlack( p_line, i_width, p_region, -2, -2);
947 DrawBlack( p_line, i_width, p_region, -2, 2);
948 DrawBlack( p_line, i_width, p_region, 2, -2);
949 DrawBlack( p_line, i_width, p_region, 2, 2);
951 DrawBlack( p_line, i_width, p_region, -3, 0);
952 DrawBlack( p_line, i_width, p_region, 0, -3);
953 DrawBlack( p_line, i_width, p_region, 3, 0);
954 DrawBlack( p_line, i_width, p_region, 0, 3);
957 for( ; p_line != NULL; p_line = p_line->p_next )
959 int i_glyph_tmax = 0;
960 int i_bitmap_offset, i_offset, i_align_offset = 0;
961 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
963 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
964 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
967 if( p_line->i_width < i_width )
969 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
971 i_align_offset = i_width - p_line->i_width;
973 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
975 i_align_offset = ( i_width - p_line->i_width ) / 2;
979 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
981 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
983 i_offset = ( p_line->p_glyph_pos[ i ].y +
984 i_glyph_tmax - p_glyph->top + 3 ) *
985 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
988 if( p_line->b_new_color_mode )
990 /* Every glyph can (and in fact must) have its own color */
991 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
994 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
996 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
998 uint8_t i_y_local = i_y;
999 uint8_t i_u_local = i_u;
1000 uint8_t i_v_local = i_v;
1002 if( p_line->p_fg_bg_ratio != 0x00 )
1004 int i_split = p_glyph->bitmap.width *
1005 p_line->p_fg_bg_ratio[ i ] / 0x7f;
1009 YUVFromRGB( p_line->p_bg_rgb[ i ],
1010 &i_y_local, &i_u_local, &i_v_local );
1014 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
1016 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
1017 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
1019 p_dst_u[i_offset+x] = i_u;
1020 p_dst_v[i_offset+x] = i_v;
1022 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1023 p_dst_a[i_offset+x] = 0xff;
1026 i_offset += i_pitch;
1029 if( p_line->pi_underline_thickness[ i ] )
1031 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1032 p_line->pi_underline_offset[ i ],
1033 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1034 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1035 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1036 i_glyph_tmax, i_align_offset,
1043 /* Apply the alpha setting */
1044 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1045 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1051 * This function renders a text subpicture region into another one.
1052 * It also calculates the size needed for this string, and renders the
1053 * needed glyphs into memory. It is used as pf_add_string callback in
1054 * the vout method by this module
1056 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1057 subpicture_region_t *p_region_in )
1059 filter_sys_t *p_sys = p_filter->p_sys;
1060 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1061 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1062 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1063 int i_string_length;
1065 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1066 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1076 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1077 psz_string = p_region_in->psz_text;
1078 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1080 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1081 i_scale = val.i_int;
1083 if( p_region_in->p_style )
1085 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1086 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1087 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1091 i_font_color = p_sys->i_font_color;
1092 i_font_alpha = 255 - p_sys->i_font_opacity;
1093 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1096 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1097 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1098 SetFontSize( p_filter, i_font_size );
1100 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1101 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1102 i_blue = i_font_color & 0x000000FF;
1104 result.x = result.y = 0;
1105 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1107 psz_unicode = psz_unicode_orig =
1108 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1109 if( psz_unicode == NULL )
1111 #if defined(WORDS_BIGENDIAN)
1112 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1114 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1116 if( iconv_handle == (vlc_iconv_t)-1 )
1118 msg_Warn( p_filter, "unable to do conversion" );
1124 const char *p_in_buffer = psz_string;
1125 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1126 i_in_bytes = strlen( psz_string );
1127 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1128 i_out_bytes_left = i_out_bytes;
1129 p_out_buffer = (char *)psz_unicode;
1130 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1132 &p_out_buffer, &i_out_bytes_left );
1134 vlc_iconv_close( iconv_handle );
1138 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1139 "bytes left %u", (unsigned)i_in_bytes );
1142 *(uint32_t*)p_out_buffer = 0;
1143 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1146 #if defined(HAVE_FRIBIDI)
1148 uint32_t *p_fribidi_string;
1149 int32_t start_pos, pos = 0;
1151 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1152 if( !p_fribidi_string )
1155 /* Do bidi conversion line-by-line */
1156 while( pos < i_string_length )
1158 while( pos < i_string_length )
1160 i_char = psz_unicode[pos];
1161 if (i_char != '\r' && i_char != '\n')
1163 p_fribidi_string[pos] = i_char;
1167 while( pos < i_string_length )
1169 i_char = psz_unicode[pos];
1170 if (i_char == '\r' || i_char == '\n')
1174 if (pos > start_pos)
1176 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1177 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1180 (FriBidiChar*)p_fribidi_string + start_pos,
1185 free( psz_unicode_orig );
1186 psz_unicode = psz_unicode_orig = p_fribidi_string;
1187 p_fribidi_string[ i_string_length ] = 0;
1191 /* Calculate relative glyph positions and a bounding box for the
1193 if( !(p_line = NewLine( strlen( psz_string ))) )
1196 i_pen_x = i_pen_y = 0;
1198 psz_line_start = psz_unicode;
1200 #define face p_sys->p_face
1201 #define glyph face->glyph
1203 while( *psz_unicode )
1205 i_char = *psz_unicode++;
1206 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1211 if( i_char == '\n' )
1213 psz_line_start = psz_unicode;
1214 if( !(p_next = NewLine( strlen( psz_string ))) )
1216 p_line->p_next = p_next;
1217 p_line->i_width = line.xMax;
1218 p_line->i_height = face->size->metrics.height >> 6;
1219 p_line->pp_glyphs[ i ] = NULL;
1220 p_line->i_alpha = i_font_alpha;
1221 p_line->i_red = i_red;
1222 p_line->i_green = i_green;
1223 p_line->i_blue = i_blue;
1226 result.x = __MAX( result.x, line.xMax );
1227 result.y += face->size->metrics.height >> 6;
1230 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1231 i_pen_y += face->size->metrics.height >> 6;
1233 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1238 i_glyph_index = FT_Get_Char_Index( face, i_char );
1239 if( p_sys->i_use_kerning && i_glyph_index
1243 FT_Get_Kerning( face, i_previous, i_glyph_index,
1244 ft_kerning_default, &delta );
1245 i_pen_x += delta.x >> 6;
1248 p_line->p_glyph_pos[ i ].x = i_pen_x;
1249 p_line->p_glyph_pos[ i ].y = i_pen_y;
1250 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1253 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1257 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1260 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1264 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1265 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1268 FT_Done_Glyph( tmp_glyph );
1271 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1274 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1275 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1276 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1278 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1279 p_line->pp_glyphs[ i ] = NULL;
1281 p_line = NewLine( strlen( psz_string ));
1282 if( p_prev ) p_prev->p_next = p_line;
1283 else p_lines = p_line;
1285 uint32_t *psz_unicode_saved = psz_unicode;
1286 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1290 if( psz_unicode == psz_line_start )
1291 { /* try harder to break that line */
1292 psz_unicode = psz_unicode_saved;
1293 while( psz_unicode > psz_line_start &&
1294 *psz_unicode != '_' && *psz_unicode != '/' &&
1295 *psz_unicode != '\\' && *psz_unicode != '.' )
1300 if( psz_unicode == psz_line_start )
1302 msg_Warn( p_filter, "unbreakable string" );
1307 *psz_unicode = '\n';
1309 psz_unicode = psz_line_start;
1312 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1315 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1316 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1318 i_previous = i_glyph_index;
1319 i_pen_x += glyph->advance.x >> 6;
1323 p_line->i_width = line.xMax;
1324 p_line->i_height = face->size->metrics.height >> 6;
1325 p_line->pp_glyphs[ i ] = NULL;
1326 p_line->i_alpha = i_font_alpha;
1327 p_line->i_red = i_red;
1328 p_line->i_green = i_green;
1329 p_line->i_blue = i_blue;
1330 result.x = __MAX( result.x, line.xMax );
1331 result.y += line.yMax - line.yMin;
1336 p_region_out->i_x = p_region_in->i_x;
1337 p_region_out->i_y = p_region_in->i_y;
1339 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1340 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1342 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1344 free( psz_unicode_orig );
1345 FreeLines( p_lines );
1349 free( psz_unicode_orig );
1350 FreeLines( p_lines );
1351 return VLC_EGENERIC;
1354 #ifdef HAVE_FONTCONFIG
1355 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1356 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1357 bool b_italic, bool b_uline )
1359 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1363 p_style->i_font_size = i_font_size;
1364 p_style->i_font_color = i_font_color;
1365 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1366 p_style->b_italic = b_italic;
1367 p_style->b_bold = b_bold;
1368 p_style->b_underline = b_uline;
1370 p_style->psz_fontname = strdup( psz_fontname );
1375 static void DeleteStyle( ft_style_t *p_style )
1379 free( p_style->psz_fontname );
1384 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1391 if(( s1->i_font_size == s2->i_font_size ) &&
1392 ( s1->i_font_color == s2->i_font_color ) &&
1393 ( s1->b_italic == s2->b_italic ) &&
1394 ( s1->b_bold == s2->b_bold ) &&
1395 ( s1->b_underline == s2->b_underline ) &&
1396 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1403 static void IconvText( filter_t *p_filter, const char *psz_string,
1404 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1406 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1408 /* If memory hasn't been allocated for our output string, allocate it here
1409 * - the calling function must now be responsible for freeing it.
1411 if( !*ppsz_unicode )
1412 *ppsz_unicode = (uint32_t *)
1413 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1415 /* We don't need to handle a NULL pointer in *ppsz_unicode
1416 * if we are instead testing for a non NULL value like we are here */
1420 #if defined(WORDS_BIGENDIAN)
1421 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1423 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1425 if( iconv_handle != (vlc_iconv_t)-1 )
1427 char *p_in_buffer, *p_out_buffer;
1428 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1429 i_in_bytes = strlen( psz_string );
1430 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1431 i_out_bytes_left = i_out_bytes;
1432 p_in_buffer = (char *) psz_string;
1433 p_out_buffer = (char *) *ppsz_unicode;
1434 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1435 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1437 vlc_iconv_close( iconv_handle );
1441 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1442 "bytes left %u", (unsigned)i_in_bytes );
1446 *(uint32_t*)p_out_buffer = 0;
1448 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1453 msg_Warn( p_filter, "unable to do conversion" );
1458 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1459 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1462 ft_style_t *p_style = NULL;
1464 char *psz_fontname = NULL;
1465 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1466 uint32_t i_karaoke_bg_color = i_font_color;
1467 int i_font_size = p_sys->i_font_size;
1469 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1470 &i_font_color, &i_karaoke_bg_color ))
1472 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1473 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1478 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1479 bool b_uline, int i_karaoke_bgcolor,
1480 line_desc_t *p_line, uint32_t *psz_unicode,
1481 int *pi_pen_x, int i_pen_y, int *pi_start,
1482 FT_Vector *p_result )
1487 bool b_first_on_line = true;
1490 int i_pen_x_start = *pi_pen_x;
1492 uint32_t *psz_unicode_start = psz_unicode;
1494 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1496 /* Account for part of line already in position */
1497 for( i=0; i<*pi_start; i++ )
1501 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1502 ft_glyph_bbox_pixels, &glyph_size );
1504 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1505 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1506 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1507 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1513 b_first_on_line = false;
1515 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1521 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1522 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1526 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1527 ft_kerning_default, &delta );
1528 *pi_pen_x += delta.x >> 6;
1530 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1531 p_line->p_glyph_pos[ i ].y = i_pen_y;
1533 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1537 "unable to render text FT_Load_Glyph returned %d", i_error );
1538 p_line->pp_glyphs[ i ] = NULL;
1539 return VLC_EGENERIC;
1541 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1545 "unable to render text FT_Get_Glyph returned %d", i_error );
1546 p_line->pp_glyphs[ i ] = NULL;
1547 return VLC_EGENERIC;
1549 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1550 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1553 FT_Done_Glyph( tmp_glyph );
1558 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1559 p_face->size->metrics.y_scale));
1560 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1561 p_face->size->metrics.y_scale));
1563 p_line->pi_underline_offset[ i ] =
1564 ( aOffset < 0 ) ? -aOffset : aOffset;
1565 p_line->pi_underline_thickness[ i ] =
1566 ( aSize < 0 ) ? -aSize : aSize;
1568 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1569 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1570 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1571 p_line->p_fg_bg_ratio[ i ] = 0x00;
1573 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1574 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1575 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1577 for( ; i >= *pi_start; i-- )
1578 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1581 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1585 if( psz_unicode == psz_unicode_start )
1587 if( b_first_on_line )
1589 msg_Warn( p_filter, "unbreakable string" );
1590 p_line->pp_glyphs[ i ] = NULL;
1591 return VLC_EGENERIC;
1593 *pi_pen_x = i_pen_x_start;
1595 p_line->i_width = line.xMax;
1596 p_line->i_height = __MAX( p_line->i_height,
1597 p_face->size->metrics.height >> 6 );
1598 p_line->pp_glyphs[ i ] = NULL;
1600 p_result->x = __MAX( p_result->x, line.xMax );
1601 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1602 i_yMax - i_yMin ) );
1607 *psz_unicode = '\n';
1609 psz_unicode = psz_unicode_start;
1610 *pi_pen_x = i_pen_x_start;
1618 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1619 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1621 i_previous = i_glyph_index;
1622 *pi_pen_x += p_face->glyph->advance.x >> 6;
1625 p_line->i_width = line.xMax;
1626 p_line->i_height = __MAX( p_line->i_height,
1627 p_face->size->metrics.height >> 6 );
1628 p_line->pp_glyphs[ i ] = NULL;
1630 p_result->x = __MAX( p_result->x, line.xMax );
1631 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1632 line.yMax - line.yMin ) );
1636 /* Get rid of any text processed - if necessary repositioning
1637 * at the start of a new line of text
1641 *psz_unicode_start = '\0';
1643 else if( psz_unicode > psz_unicode_start )
1645 for( i=0; psz_unicode[ i ]; i++ )
1646 psz_unicode_start[ i ] = psz_unicode[ i ];
1647 psz_unicode_start[ i ] = '\0';
1653 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1654 uint32_t **psz_text_out, uint32_t *pi_runs,
1655 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1656 ft_style_t *p_style )
1658 uint32_t i_string_length = 0;
1660 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1661 *psz_text_out += i_string_length;
1663 if( ppp_styles && ppi_run_lengths )
1669 *ppp_styles = (ft_style_t **)
1670 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1672 else if( *pi_runs == 1 )
1674 *ppp_styles = (ft_style_t **)
1675 malloc( *pi_runs * sizeof( ft_style_t * ) );
1678 /* We have just malloc'ed this memory successfully -
1679 * *pi_runs HAS to be within the memory area of *ppp_styles */
1682 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1686 if( *ppi_run_lengths )
1688 *ppi_run_lengths = (uint32_t *)
1689 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1691 else if( *pi_runs == 1 )
1693 *ppi_run_lengths = (uint32_t *)
1694 malloc( *pi_runs * sizeof( uint32_t ) );
1697 /* same remarks here */
1698 if( *ppi_run_lengths )
1700 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1703 /* If we couldn't use the p_style argument due to memory allocation
1704 * problems above, release it here.
1706 if( p_style ) DeleteStyle( p_style );
1709 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1713 for( k=0; k < p_sys->i_font_attachments; k++ )
1715 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1717 FT_Face p_face = NULL;
1719 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1727 bool match = !strcasecmp( p_face->family_name,
1728 p_style->psz_fontname );
1730 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1731 match = match && p_style->b_bold;
1733 match = match && !p_style->b_bold;
1735 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1736 match = match && p_style->b_italic;
1738 match = match && !p_style->b_italic;
1746 FT_Done_Face( p_face );
1751 return VLC_EGENERIC;
1754 static int ProcessLines( filter_t *p_filter,
1759 uint32_t *pi_run_lengths,
1760 ft_style_t **pp_styles,
1761 line_desc_t **pp_lines,
1763 FT_Vector *p_result,
1767 uint32_t *pi_k_run_lengths,
1768 uint32_t *pi_k_durations )
1770 filter_sys_t *p_sys = p_filter->p_sys;
1771 ft_style_t **pp_char_styles;
1772 int *p_new_positions = NULL;
1773 int8_t *p_levels = NULL;
1774 uint8_t *pi_karaoke_bar = NULL;
1778 /* Assign each character in the text string its style explicitly, so that
1779 * after the characters have been shuffled around by Fribidi, we can re-apply
1780 * the styles, and to simplify the calculation of runs within a line.
1782 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1783 if( !pp_char_styles )
1788 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1789 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1790 * we just won't be able to display the progress bar; at least we'll
1796 for( j = 0; j < i_runs; j++ )
1797 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1798 pp_char_styles[ i++ ] = pp_styles[ j ];
1800 #if defined(HAVE_FRIBIDI)
1802 ft_style_t **pp_char_styles_new;
1803 int *p_old_positions;
1804 uint32_t *p_fribidi_string;
1805 int start_pos, pos = 0;
1807 pp_char_styles_new = (ft_style_t **)
1808 malloc( i_len * sizeof( ft_style_t * ));
1810 p_fribidi_string = (uint32_t *)
1811 malloc( (i_len + 1) * sizeof(uint32_t) );
1812 p_old_positions = (int *)
1813 malloc( (i_len + 1) * sizeof( int ) );
1814 p_new_positions = (int *)
1815 malloc( (i_len + 1) * sizeof( int ) );
1816 p_levels = (int8_t *)
1817 malloc( (i_len + 1) * sizeof( int8_t ) );
1819 if( ! pp_char_styles_new ||
1820 ! p_fribidi_string ||
1821 ! p_old_positions ||
1822 ! p_new_positions ||
1826 free( p_old_positions );
1827 free( p_new_positions );
1828 free( p_fribidi_string );
1829 free( pp_char_styles_new );
1830 free( pi_karaoke_bar );
1832 free( pp_char_styles );
1836 /* Do bidi conversion line-by-line */
1839 while(pos < i_len) {
1840 if (psz_text[pos] != '\n')
1842 p_fribidi_string[pos] = psz_text[pos];
1843 pp_char_styles_new[pos] = pp_char_styles[pos];
1844 p_new_positions[pos] = pos;
1849 while(pos < i_len) {
1850 if (psz_text[pos] == '\n')
1854 if (pos > start_pos)
1856 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1857 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1858 pos - start_pos, &base_dir,
1859 (FriBidiChar*)p_fribidi_string + start_pos,
1860 p_new_positions + start_pos,
1862 p_levels + start_pos );
1863 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1865 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1866 p_old_positions[ j - start_pos ] ];
1867 p_new_positions[ j ] += start_pos;
1871 free( p_old_positions );
1872 free( pp_char_styles );
1873 pp_char_styles = pp_char_styles_new;
1874 psz_text = p_fribidi_string;
1875 p_fribidi_string[ i_len ] = 0;
1878 /* Work out the karaoke */
1879 if( pi_karaoke_bar )
1881 int64_t i_last_duration = 0;
1882 int64_t i_duration = 0;
1883 int64_t i_start_pos = 0;
1884 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1886 for( k = 0; k< i_k_runs; k++ )
1888 double fraction = 0.0;
1890 i_duration += pi_k_durations[ k ];
1892 if( i_duration < i_elapsed )
1894 /* Completely finished this run-length -
1895 * let it render normally */
1899 else if( i_elapsed < i_last_duration )
1901 /* Haven't got up to this segment yet -
1902 * render it completely in karaoke BG mode */
1908 /* Partway through this run */
1910 fraction = (double)(i_elapsed - i_last_duration) /
1911 (double)pi_k_durations[ k ];
1913 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1915 double shade = pi_k_run_lengths[ k ] * fraction;
1917 if( p_new_positions )
1918 j = p_new_positions[ i_start_pos + i ];
1920 j = i_start_pos + i;
1922 if( i < (uint32_t)shade )
1923 pi_karaoke_bar[ j ] = 0xff;
1924 else if( (double)i > shade )
1925 pi_karaoke_bar[ j ] = 0x00;
1928 shade -= (int)shade;
1929 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1930 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1934 i_last_duration = i_duration;
1935 i_start_pos += pi_k_run_lengths[ k ];
1939 free( p_new_positions );
1941 FT_Vector tmp_result;
1943 line_desc_t *p_line = NULL;
1944 line_desc_t *p_prev = NULL;
1950 p_result->x = p_result->y = 0;
1951 tmp_result.x = tmp_result.y = 0;
1954 for( k = 0; k <= (uint32_t) i_len; k++ )
1956 if( ( k == (uint32_t) i_len ) ||
1958 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1960 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1962 /* End of the current style run */
1963 FT_Face p_face = NULL;
1966 /* Look for a match amongst our attachments first */
1967 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1971 char *psz_fontfile = NULL;
1973 psz_fontfile = FontConfig_Select( NULL,
1974 p_style->psz_fontname,
1978 if( psz_fontfile && ! *psz_fontfile )
1980 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1981 " so using default font", p_style->psz_fontname,
1982 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1983 (p_style->b_bold ? "(Bold)" :
1984 (p_style->b_italic ? "(Italic)" : ""))) );
1985 free( psz_fontfile );
1986 psz_fontfile = NULL;
1991 if( FT_New_Face( p_sys->p_library,
1992 psz_fontfile, i_idx, &p_face ) )
1994 free( psz_fontfile );
1995 free( pp_char_styles );
1996 #if defined(HAVE_FRIBIDI)
1999 free( pi_karaoke_bar );
2000 return VLC_EGENERIC;
2002 free( psz_fontfile );
2006 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2008 /* We've loaded a font face which is unhelpful for actually
2009 * rendering text - fallback to the default one.
2011 FT_Done_Face( p_face );
2015 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2016 ft_encoding_unicode ) ||
2017 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2018 p_style->i_font_size ) )
2020 if( p_face ) FT_Done_Face( p_face );
2021 free( pp_char_styles );
2022 #if defined(HAVE_FRIBIDI)
2025 free( pi_karaoke_bar );
2026 return VLC_EGENERIC;
2028 p_sys->i_use_kerning =
2029 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2032 uint32_t *psz_unicode = (uint32_t *)
2033 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2036 if( p_face ) FT_Done_Face( p_face );
2037 free( pp_char_styles );
2038 free( psz_unicode );
2039 #if defined(HAVE_FRIBIDI)
2042 free( pi_karaoke_bar );
2045 memcpy( psz_unicode, psz_text + i_prev,
2046 sizeof( uint32_t ) * ( k - i_prev ) );
2047 psz_unicode[ k - i_prev ] = 0;
2048 while( *psz_unicode )
2052 if( !(p_line = NewLine( i_len - i_prev)) )
2054 if( p_face ) FT_Done_Face( p_face );
2055 free( pp_char_styles );
2056 free( psz_unicode );
2057 #if defined(HAVE_FRIBIDI)
2060 free( pi_karaoke_bar );
2063 /* New Color mode only works in YUVA rendering mode --
2064 * (RGB mode has palette constraints on it). We therefore
2065 * need to populate the legacy colour fields also.
2067 p_line->b_new_color_mode = true;
2068 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2069 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2070 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2071 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2072 p_line->p_next = NULL;
2074 i_pen_y += tmp_result.y;
2078 if( p_prev ) p_prev->p_next = p_line;
2079 else *pp_lines = p_line;
2082 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2083 p_style->i_font_color, p_style->b_underline,
2084 p_style->i_karaoke_bg_color,
2085 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2086 &tmp_result ) != VLC_SUCCESS )
2088 if( p_face ) FT_Done_Face( p_face );
2089 free( pp_char_styles );
2090 free( psz_unicode );
2091 #if defined(HAVE_FRIBIDI)
2094 free( pi_karaoke_bar );
2095 return VLC_EGENERIC;
2100 p_result->x = __MAX( p_result->x, tmp_result.x );
2101 p_result->y += tmp_result.y;
2106 if( *psz_unicode == '\n')
2110 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2112 *c_ptr = *(c_ptr+1);
2117 free( psz_unicode );
2118 if( p_face ) FT_Done_Face( p_face );
2122 free( pp_char_styles );
2123 #if defined(HAVE_FRIBIDI)
2128 p_result->x = __MAX( p_result->x, tmp_result.x );
2129 p_result->y += tmp_result.y;
2132 if( pi_karaoke_bar )
2135 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2137 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2139 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2143 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2145 /* 100% BG colour will render faster if we
2146 * instead make it 100% FG colour, so leave
2147 * the ratio alone and copy the value across
2149 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2153 if( pi_karaoke_bar[ i ] & 0x80 )
2155 /* Swap Left and Right sides over for Right aligned
2156 * language text (eg. Arabic, Hebrew)
2158 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2160 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2161 p_line->p_bg_rgb[ k ] = i_tmp;
2163 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2166 /* Jump over the '\n' at the line-end */
2169 free( pi_karaoke_bar );
2175 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2176 subpicture_region_t *p_region_in )
2178 int rv = VLC_SUCCESS;
2179 stream_t *p_sub = NULL;
2180 xml_reader_t *p_xml_reader = NULL;
2182 if( !p_region_in || !p_region_in->psz_html )
2183 return VLC_EGENERIC;
2185 /* Reset the default fontsize in case screen metrics have changed */
2186 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2188 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2189 (uint8_t *) p_region_in->psz_html,
2190 strlen( p_region_in->psz_html ),
2194 if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
2195 if( p_filter->p_sys->p_xml )
2197 bool b_karaoke = false;
2199 p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
2202 /* Look for Root Node */
2203 if( xml_ReaderRead( p_xml_reader ) == 1 )
2205 char *psz_node = xml_ReaderName( p_xml_reader );
2207 if( !strcasecmp( "karaoke", psz_node ) )
2209 /* We're going to have to render the text a number
2210 * of times to show the progress marker on the text.
2212 var_SetBool( p_filter, "text-rerender", true );
2215 else if( !strcasecmp( "text", psz_node ) )
2221 /* Only text and karaoke tags are supported */
2222 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2223 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2224 p_xml_reader = NULL;
2236 uint32_t i_runs = 0;
2237 uint32_t i_k_runs = 0;
2238 uint32_t *pi_run_lengths = NULL;
2239 uint32_t *pi_k_run_lengths = NULL;
2240 uint32_t *pi_k_durations = NULL;
2241 ft_style_t **pp_styles = NULL;
2243 line_desc_t *p_lines = NULL;
2245 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2246 sizeof( uint32_t ) );
2251 rv = ProcessNodes( p_filter, p_xml_reader,
2252 p_region_in->p_style, psz_text, &i_len,
2253 &i_runs, &pi_run_lengths, &pp_styles,
2255 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2258 p_region_out->i_x = p_region_in->i_x;
2259 p_region_out->i_y = p_region_in->i_y;
2261 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2263 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2264 pi_run_lengths, pp_styles, &p_lines, &result,
2265 b_karaoke, i_k_runs, pi_k_run_lengths,
2269 for( k=0; k<i_runs; k++)
2270 DeleteStyle( pp_styles[k] );
2272 free( pi_run_lengths );
2275 /* Don't attempt to render text that couldn't be layed out
2278 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2280 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2282 Render( p_filter, p_region_out, p_lines,
2283 result.x, result.y );
2287 RenderYUVA( p_filter, p_region_out, p_lines,
2288 result.x, result.y );
2292 FreeLines( p_lines );
2294 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
2297 stream_Delete( p_sub );
2303 static char* FontConfig_Select( FcConfig* priv, const char* family,
2304 bool b_bold, bool b_italic, int *i_idx )
2307 FcPattern *pat, *p_pat;
2311 pat = FcPatternCreate();
2312 if (!pat) return NULL;
2314 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2315 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2316 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2317 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2319 FcDefaultSubstitute( pat );
2321 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2323 FcPatternDestroy( pat );
2327 p_pat = FcFontMatch( priv, pat, &result );
2328 FcPatternDestroy( pat );
2329 if( !p_pat ) return NULL;
2331 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2332 || ( val_b != FcTrue ) )
2334 FcPatternDestroy( p_pat );
2337 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2342 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2344 FcPatternDestroy( p_pat );
2349 if( strcasecmp((const char*)val_s, family ) != 0 )
2350 msg_Warn( p_filter, "fontconfig: selected font family is not"
2351 "the requested one: '%s' != '%s'\n",
2352 (const char*)val_s, family );
2355 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2357 FcPatternDestroy( p_pat );
2361 FcPatternDestroy( p_pat );
2362 return strdup( (const char*)val_s );
2366 static void FreeLine( line_desc_t *p_line )
2369 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2371 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2373 free( p_line->pp_glyphs );
2374 free( p_line->p_glyph_pos );
2375 free( p_line->p_fg_rgb );
2376 free( p_line->p_bg_rgb );
2377 free( p_line->p_fg_bg_ratio );
2378 free( p_line->pi_underline_offset );
2379 free( p_line->pi_underline_thickness );
2383 static void FreeLines( line_desc_t *p_lines )
2385 line_desc_t *p_line, *p_next;
2387 if( !p_lines ) return;
2389 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2391 p_next = p_line->p_next;
2396 static line_desc_t *NewLine( int i_count )
2398 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2400 if( !p_line ) return NULL;
2401 p_line->i_height = 0;
2402 p_line->i_width = 0;
2403 p_line->p_next = NULL;
2405 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2406 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2407 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2408 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2409 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2410 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2411 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2412 if( ( p_line->pp_glyphs == NULL ) ||
2413 ( p_line->p_glyph_pos == NULL ) ||
2414 ( p_line->p_fg_rgb == NULL ) ||
2415 ( p_line->p_bg_rgb == NULL ) ||
2416 ( p_line->p_fg_bg_ratio == NULL ) ||
2417 ( p_line->pi_underline_offset == NULL ) ||
2418 ( p_line->pi_underline_thickness == NULL ) )
2420 free( p_line->pi_underline_thickness );
2421 free( p_line->pi_underline_offset );
2422 free( p_line->p_fg_rgb );
2423 free( p_line->p_bg_rgb );
2424 free( p_line->p_fg_bg_ratio );
2425 free( p_line->p_glyph_pos );
2426 free( p_line->pp_glyphs );
2430 p_line->pp_glyphs[0] = NULL;
2431 p_line->b_new_color_mode = false;
2436 static int GetFontSize( filter_t *p_filter )
2438 filter_sys_t *p_sys = p_filter->p_sys;
2442 if( p_sys->i_default_font_size )
2444 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2445 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2447 i_size = p_sys->i_default_font_size;
2451 var_Get( p_filter, "freetype-rel-fontsize", &val );
2454 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2455 p_filter->p_sys->i_display_height =
2456 p_filter->fmt_out.video.i_height;
2461 msg_Warn( p_filter, "invalid fontsize, using 12" );
2462 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2463 i_size = 12 * val.i_int / 1000;
2470 static int SetFontSize( filter_t *p_filter, int i_size )
2472 filter_sys_t *p_sys = p_filter->p_sys;
2476 i_size = GetFontSize( p_filter );
2478 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2481 p_sys->i_font_size = i_size;
2483 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2485 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2486 return VLC_EGENERIC;
2492 static void YUVFromRGB( uint32_t i_argb,
2493 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2495 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2496 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2497 int i_blue = ( i_argb & 0x000000ff );
2499 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2500 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2501 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2502 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2503 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2504 -585 * i_blue + 4096 + 1048576) >> 13, 240);