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 *****************************************************************************/
31 #ifdef HAVE_LINUX_LIMITS_H
32 # include <linux/limits.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
42 #include <vlc_input.h>
51 #include FT_FREETYPE_H
53 #define FT_FLOOR(X) ((X & -64) >> 6)
54 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
55 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
59 #define FC_DEFAULT_FONT "Lucida Grande"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
67 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
68 #define FC_DEFAULT_FONT "Serif Bold"
71 #if defined(HAVE_FRIBIDI)
72 #include <fribidi/fribidi.h>
75 #ifdef HAVE_FONTCONFIG
76 #include <fontconfig/fontconfig.h>
79 typedef struct line_desc_t line_desc_t;
81 /*****************************************************************************
83 *****************************************************************************/
84 static int Create ( vlc_object_t * );
85 static void Destroy( vlc_object_t * );
87 static int LoadFontsFromAttachments( filter_t *p_filter );
89 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
90 static int RenderText( filter_t *, subpicture_region_t *,
91 subpicture_region_t * );
92 #ifdef HAVE_FONTCONFIG
93 static int RenderHtml( filter_t *, subpicture_region_t *,
94 subpicture_region_t * );
95 static char *FontConfig_Select( FcConfig *, const char *,
96 vlc_bool_t, vlc_bool_t, int * );
98 static line_desc_t *NewLine( int );
100 static int GetFontSize( filter_t *p_filter );
101 static int SetFontSize( filter_t *, int );
102 static void YUVFromRGB( uint32_t i_argb,
103 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
105 /*****************************************************************************
107 *****************************************************************************/
108 #define FONT_TEXT N_("Font")
109 #define FONT_LONGTEXT N_("Filename for the font you want to use")
110 #define FONTSIZE_TEXT N_("Font size in pixels")
111 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
112 "that will be rendered on the video. " \
113 "If set to something different than 0 this option will override the " \
114 "relative font size." )
115 #define OPACITY_TEXT N_("Opacity")
116 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
117 "text that will be rendered on the video. 0 = transparent, " \
118 "255 = totally opaque. " )
119 #define COLOR_TEXT N_("Text default color")
120 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
121 "the video. This must be an hexadecimal (like HTML colors). The first two "\
122 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
123 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
124 #define FONTSIZER_TEXT N_("Relative font size")
125 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
126 "fonts that will be rendered on the video. If absolute font size is set, "\
127 "relative size will be overriden." )
129 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
130 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
131 N_("Large"), N_("Larger") };
132 #define YUVP_TEXT N_("Use YUVP renderer")
133 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
134 "This option is only needed if you want to encode into DVB subtitles" )
135 #define EFFECT_TEXT N_("Font Effect")
136 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
137 "text to improve its readability." )
139 #define EFFECT_BACKGROUND 1
140 #define EFFECT_OUTLINE 2
141 #define EFFECT_OUTLINE_FAT 3
143 static int pi_effects[] = { 1, 2, 3 };
144 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
146 static int pi_color_values[] = {
147 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
148 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
149 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
151 static const char *ppsz_color_descriptions[] = {
152 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
153 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
154 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
157 set_shortname( _("Text renderer"));
158 set_description( _("Freetype2 font renderer") );
159 set_category( CAT_VIDEO );
160 set_subcategory( SUBCAT_VIDEO_SUBPIC );
162 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
165 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
166 FONTSIZE_LONGTEXT, VLC_TRUE );
168 /* opacity valid on 0..255, with default 255 = fully opaque */
169 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
170 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
172 /* hook to the color values list, with default 0x00ffffff = white */
173 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
174 COLOR_LONGTEXT, VLC_FALSE );
175 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
177 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
178 FONTSIZER_LONGTEXT, VLC_FALSE );
179 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
180 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
181 EFFECT_LONGTEXT, VLC_FALSE );
182 change_integer_list( pi_effects, ppsz_effects_text, 0 );
184 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
185 YUVP_LONGTEXT, VLC_TRUE );
186 set_capability( "text renderer", 100 );
187 add_shortcut( "text" );
188 set_callbacks( Create, Destroy );
193 /** NULL-terminated list of glyphs making the string */
194 FT_BitmapGlyph *pp_glyphs;
195 /** list of relative positions for the glyphs */
196 FT_Vector *p_glyph_pos;
197 /** list of RGB information for styled text
198 * -- if the rendering mode supports it (RenderYUVA) and
199 * b_new_color_mode is set, then it becomes possible to
200 * have multicoloured text within the subtitles. */
203 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
204 vlc_bool_t b_new_color_mode;
205 /** underline information -- only supplied if text should be underlined */
206 uint16_t *pi_underline_offset;
207 uint16_t *pi_underline_thickness;
211 int i_red, i_green, i_blue;
217 typedef struct font_stack_t font_stack_t;
222 uint32_t i_color; /* ARGB */
223 uint32_t i_karaoke_bg_color; /* ARGB */
225 font_stack_t *p_next;
231 uint32_t i_font_color; /* ARGB */
232 uint32_t i_karaoke_bg_color; /* ARGB */
235 vlc_bool_t b_underline;
239 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
240 static void FreeLines( line_desc_t * );
241 static void FreeLine( line_desc_t * );
243 /*****************************************************************************
244 * filter_sys_t: freetype local data
245 *****************************************************************************
246 * This structure is part of the video output thread descriptor.
247 * It describes the freetype specific properties of an output thread.
248 *****************************************************************************/
251 FT_Library p_library; /* handle to library */
252 FT_Face p_face; /* handle to face object */
253 vlc_bool_t i_use_kerning;
254 uint8_t i_font_opacity;
259 int i_default_font_size;
260 int i_display_height;
261 #ifdef HAVE_FONTCONFIG
262 FcConfig *p_fontconfig;
265 input_attachment_t **pp_font_attachments;
266 int i_font_attachments;
269 /*****************************************************************************
270 * Create: allocates osd-text video thread output method
271 *****************************************************************************
272 * This function allocates and initializes a Clone vout method.
273 *****************************************************************************/
274 static int Create( vlc_object_t *p_this )
276 filter_t *p_filter = (filter_t *)p_this;
278 char *psz_fontfile = NULL;
282 /* Allocate structure */
283 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
286 msg_Err( p_filter, "out of memory" );
290 p_sys->p_library = 0;
291 p_sys->i_font_size = 0;
292 p_sys->i_display_height = 0;
294 var_Create( p_filter, "freetype-font",
295 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
296 var_Create( p_filter, "freetype-fontsize",
297 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
298 var_Create( p_filter, "freetype-rel-fontsize",
299 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
300 var_Create( p_filter, "freetype-opacity",
301 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
302 var_Create( p_filter, "freetype-effect",
303 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
304 var_Get( p_filter, "freetype-opacity", &val );
305 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
306 var_Create( p_filter, "freetype-color",
307 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
308 var_Get( p_filter, "freetype-color", &val );
309 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
310 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
312 /* Look what method was requested */
313 var_Get( p_filter, "freetype-font", &val );
314 psz_fontfile = val.psz_string;
315 if( !psz_fontfile || !*psz_fontfile )
317 if( psz_fontfile ) free( psz_fontfile );
318 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
321 msg_Err( p_filter, "out of memory" );
325 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
326 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
327 #elif defined(__APPLE__)
328 strcpy( psz_fontfile, DEFAULT_FONT );
330 msg_Err( p_filter, "user didn't specify a font" );
335 #ifdef HAVE_FONTCONFIG
337 p_sys->p_fontconfig = FcConfigGetCurrent();
339 p_sys->p_fontconfig = NULL;
341 i_error = FT_Init_FreeType( &p_sys->p_library );
344 msg_Err( p_filter, "couldn't initialize freetype" );
347 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
349 if( i_error == FT_Err_Unknown_File_Format )
351 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
356 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
360 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
363 msg_Err( p_filter, "font has no unicode translation table" );
367 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
369 var_Get( p_filter, "freetype-fontsize", &val );
370 p_sys->i_default_font_size = val.i_int;
371 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
373 if( psz_fontfile ) free( psz_fontfile );
375 p_sys->pp_font_attachments = NULL;
376 p_sys->i_font_attachments = 0;
378 p_filter->pf_render_text = RenderText;
379 #ifdef HAVE_FONTCONFIG
380 p_filter->pf_render_html = RenderHtml;
382 p_filter->pf_render_html = NULL;
385 LoadFontsFromAttachments( p_filter );
390 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
391 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
392 if( psz_fontfile ) free( psz_fontfile );
397 /*****************************************************************************
398 * Destroy: destroy Clone video thread output method
399 *****************************************************************************
400 * Clean up all data and library connections
401 *****************************************************************************/
402 static void Destroy( vlc_object_t *p_this )
404 filter_t *p_filter = (filter_t *)p_this;
405 filter_sys_t *p_sys = p_filter->p_sys;
407 if( p_sys->pp_font_attachments )
411 for( k = 0; k < p_sys->i_font_attachments; k++ )
413 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
416 free( p_sys->pp_font_attachments );
419 #ifdef HAVE_FONTCONFIG
420 FcConfigDestroy( p_sys->p_fontconfig );
421 p_sys->p_fontconfig = NULL;
422 /* FcFini asserts calling the subfunction FcCacheFini()
423 * even if no other library functions have been made since FcInit(),
424 * so don't call it. */
426 FT_Done_Face( p_sys->p_face );
427 FT_Done_FreeType( p_sys->p_library );
431 /*****************************************************************************
432 * Make any TTF/OTF fonts present in the attachments of the media file
433 * and store them for later use by the FreeType Engine
434 *****************************************************************************/
435 static int LoadFontsFromAttachments( filter_t *p_filter )
437 filter_sys_t *p_sys = p_filter->p_sys;
438 input_thread_t *p_input;
439 input_attachment_t **pp_attachments;
440 int i_attachments_cnt;
442 int rv = VLC_SUCCESS;
444 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
448 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
451 p_sys->i_font_attachments = 0;
452 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
453 if(! p_sys->pp_font_attachments )
456 for( k = 0; k < i_attachments_cnt; k++ )
458 input_attachment_t *p_attach = pp_attachments[k];
460 if( p_sys->pp_font_attachments )
462 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
463 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
464 ( p_attach->i_data > 0 ) &&
465 ( p_attach->p_data != NULL ) )
467 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
471 vlc_input_attachment_Delete( p_attach );
476 vlc_input_attachment_Delete( p_attach );
479 free( pp_attachments );
484 /*****************************************************************************
485 * Render: place string in picture
486 *****************************************************************************
487 * This function merges the previously rendered freetype glyphs into a picture
488 *****************************************************************************/
489 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
490 line_desc_t *p_line, int i_width, int i_height )
492 static uint8_t pi_gamma[16] =
493 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
494 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
498 int i, x, y, i_pitch;
499 uint8_t i_y; /* YUV values, derived from incoming RGB */
501 subpicture_region_t *p_region_tmp;
503 /* Create a new subpicture region */
504 memset( &fmt, 0, sizeof(video_format_t) );
505 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
507 fmt.i_width = fmt.i_visible_width = i_width + 4;
508 fmt.i_height = fmt.i_visible_height = i_height + 4;
509 if( p_region->fmt.i_visible_width > 0 )
510 fmt.i_visible_width = p_region->fmt.i_visible_width;
511 if( p_region->fmt.i_visible_height > 0 )
512 fmt.i_visible_height = p_region->fmt.i_visible_height;
513 fmt.i_x_offset = fmt.i_y_offset = 0;
514 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
517 msg_Err( p_filter, "cannot allocate SPU region" );
521 p_region->fmt = p_region_tmp->fmt;
522 p_region->picture = p_region_tmp->picture;
523 free( p_region_tmp );
525 /* Calculate text color components */
526 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
527 25 * p_line->i_blue + 128) >> 8) + 16;
528 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
529 112 * p_line->i_blue + 128) >> 8) + 128;
530 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
531 18 * p_line->i_blue + 128) >> 8) + 128;
534 fmt.p_palette->i_entries = 16;
535 for( i = 0; i < 8; i++ )
537 fmt.p_palette->palette[i][0] = 0;
538 fmt.p_palette->palette[i][1] = 0x80;
539 fmt.p_palette->palette[i][2] = 0x80;
540 fmt.p_palette->palette[i][3] = pi_gamma[i];
541 fmt.p_palette->palette[i][3] =
542 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
544 for( i = 8; i < fmt.p_palette->i_entries; i++ )
546 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
547 fmt.p_palette->palette[i][1] = i_u;
548 fmt.p_palette->palette[i][2] = i_v;
549 fmt.p_palette->palette[i][3] = pi_gamma[i];
550 fmt.p_palette->palette[i][3] =
551 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
554 p_dst = p_region->picture.Y_PIXELS;
555 i_pitch = p_region->picture.Y_PITCH;
557 /* Initialize the region pixels */
558 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
560 for( ; p_line != NULL; p_line = p_line->p_next )
562 int i_glyph_tmax = 0;
563 int i_bitmap_offset, i_offset, i_align_offset = 0;
564 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
566 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
567 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
570 if( p_line->i_width < i_width )
572 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
574 i_align_offset = i_width - p_line->i_width;
576 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
578 i_align_offset = ( i_width - p_line->i_width ) / 2;
582 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
584 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
586 i_offset = ( p_line->p_glyph_pos[ i ].y +
587 i_glyph_tmax - p_glyph->top + 2 ) *
588 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
591 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
593 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
595 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
597 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
604 /* Outlining (find something better than nearest neighbour filtering ?) */
607 uint8_t *p_dst = p_region->picture.Y_PIXELS;
608 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
609 uint8_t left, current;
611 for( y = 1; y < (int)fmt.i_height - 1; y++ )
613 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
614 p_dst += p_region->picture.Y_PITCH;
617 for( x = 1; x < (int)fmt.i_width - 1; x++ )
620 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
621 p_dst[x -1 + p_region->picture.Y_PITCH ] + p_dst[x + p_region->picture.Y_PITCH] + p_dst[x + 1 + p_region->picture.Y_PITCH]) / 16;
625 memset( p_top, 0, fmt.i_width );
631 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
632 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
633 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
634 int i_glyph_tmax, int i_align_offset,
635 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
636 subpicture_region_t *p_region)
640 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
642 p_dst_y = p_region->picture.Y_PIXELS;
643 p_dst_u = p_region->picture.U_PIXELS;
644 p_dst_v = p_region->picture.V_PIXELS;
645 p_dst_a = p_region->picture.A_PIXELS;
646 i_pitch = p_region->picture.A_PITCH;
648 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
649 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
651 for( y = 0; y < i_line_thickness; y++ )
653 int i_extra = p_this_glyph->bitmap.width;
657 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
658 (p_this_glyph_pos->x + p_this_glyph->left);
660 for( x = 0; x < i_extra; x++ )
662 vlc_bool_t b_ok = VLC_TRUE;
664 /* break the underline around the tails of any glyphs which cross it */
665 for( z = x - i_line_thickness;
666 z < x + i_line_thickness && b_ok;
669 if( p_next_glyph && ( z >= i_extra ) )
671 int i_row = i_line_offset + p_next_glyph->top + y;
673 if( ( p_next_glyph->bitmap.rows > i_row ) &&
674 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
679 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
681 int i_row = i_line_offset + p_this_glyph->top + y;
683 if( ( p_this_glyph->bitmap.rows > i_row ) &&
684 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
693 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
694 p_dst_u[i_offset+x] = i_u;
695 p_dst_v[i_offset+x] = i_v;
696 p_dst_a[i_offset+x] = 255;
703 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
705 uint8_t *p_dst = p_region->picture.A_PIXELS;
706 int i_pitch = p_region->picture.A_PITCH;
709 for( ; p_line != NULL; p_line = p_line->p_next )
711 int i_glyph_tmax=0, i = 0;
712 int i_bitmap_offset, i_offset, i_align_offset = 0;
713 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
715 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
716 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
719 if( p_line->i_width < i_width )
721 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
723 i_align_offset = i_width - p_line->i_width;
725 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
727 i_align_offset = ( i_width - p_line->i_width ) / 2;
731 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
733 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
735 i_offset = ( p_line->p_glyph_pos[ i ].y +
736 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
737 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
738 i_align_offset +xoffset;
740 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
742 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
744 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
745 if( p_dst[i_offset+x] <
746 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
748 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
757 /*****************************************************************************
758 * Render: place string in picture
759 *****************************************************************************
760 * This function merges the previously rendered freetype glyphs into a picture
761 *****************************************************************************/
762 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
763 line_desc_t *p_line, int i_width, int i_height )
765 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
767 int i, x, y, i_pitch, i_alpha;
768 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
769 subpicture_region_t *p_region_tmp;
771 if( i_width == 0 || i_height == 0 )
774 /* Create a new subpicture region */
775 memset( &fmt, 0, sizeof(video_format_t) );
776 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
778 fmt.i_width = fmt.i_visible_width = i_width + 6;
779 fmt.i_height = fmt.i_visible_height = i_height + 6;
780 if( p_region->fmt.i_visible_width > 0 )
781 fmt.i_visible_width = p_region->fmt.i_visible_width;
782 if( p_region->fmt.i_visible_height > 0 )
783 fmt.i_visible_height = p_region->fmt.i_visible_height;
784 fmt.i_x_offset = fmt.i_y_offset = 0;
785 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
788 msg_Err( p_filter, "cannot allocate SPU region" );
792 p_region->fmt = p_region_tmp->fmt;
793 p_region->picture = p_region_tmp->picture;
794 free( p_region_tmp );
796 /* Calculate text color components */
797 YUVFromRGB( (p_line->i_red << 16) |
798 (p_line->i_green << 8) |
801 i_alpha = p_line->i_alpha;
803 p_dst_y = p_region->picture.Y_PIXELS;
804 p_dst_u = p_region->picture.U_PIXELS;
805 p_dst_v = p_region->picture.V_PIXELS;
806 p_dst_a = p_region->picture.A_PIXELS;
807 i_pitch = p_region->picture.A_PITCH;
809 /* Initialize the region pixels */
810 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
812 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
813 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
814 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
815 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
819 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
820 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
821 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
822 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
824 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
825 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
827 DrawBlack( p_line, i_width, p_region, 0, 0);
828 DrawBlack( p_line, i_width, p_region, -1, 0);
829 DrawBlack( p_line, i_width, p_region, 0, -1);
830 DrawBlack( p_line, i_width, p_region, 1, 0);
831 DrawBlack( p_line, i_width, p_region, 0, 1);
834 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
836 DrawBlack( p_line, i_width, p_region, -1, -1);
837 DrawBlack( p_line, i_width, p_region, -1, 1);
838 DrawBlack( p_line, i_width, p_region, 1, -1);
839 DrawBlack( p_line, i_width, p_region, 1, 1);
841 DrawBlack( p_line, i_width, p_region, -2, 0);
842 DrawBlack( p_line, i_width, p_region, 0, -2);
843 DrawBlack( p_line, i_width, p_region, 2, 0);
844 DrawBlack( p_line, i_width, p_region, 0, 2);
846 DrawBlack( p_line, i_width, p_region, -2, -2);
847 DrawBlack( p_line, i_width, p_region, -2, 2);
848 DrawBlack( p_line, i_width, p_region, 2, -2);
849 DrawBlack( p_line, i_width, p_region, 2, 2);
851 DrawBlack( p_line, i_width, p_region, -3, 0);
852 DrawBlack( p_line, i_width, p_region, 0, -3);
853 DrawBlack( p_line, i_width, p_region, 3, 0);
854 DrawBlack( p_line, i_width, p_region, 0, 3);
857 for( ; p_line != NULL; p_line = p_line->p_next )
859 int i_glyph_tmax = 0;
860 int i_bitmap_offset, i_offset, i_align_offset = 0;
861 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
863 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
864 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
867 if( p_line->i_width < i_width )
869 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
871 i_align_offset = i_width - p_line->i_width;
873 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
875 i_align_offset = ( i_width - p_line->i_width ) / 2;
879 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
881 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
883 i_offset = ( p_line->p_glyph_pos[ i ].y +
884 i_glyph_tmax - p_glyph->top + 3 ) *
885 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
888 if( p_line->b_new_color_mode )
890 /* Every glyph can (and in fact must) have its own color */
891 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
894 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
896 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
898 uint8_t i_y_local = i_y;
899 uint8_t i_u_local = i_u;
900 uint8_t i_v_local = i_v;
902 if( p_line->p_fg_bg_ratio != 0x00 )
904 int i_split = p_glyph->bitmap.width *
905 p_line->p_fg_bg_ratio[ i ] / 0x7f;
909 YUVFromRGB( p_line->p_bg_rgb[ i ],
910 &i_y_local, &i_u_local, &i_v_local );
914 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
916 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
917 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
919 p_dst_u[i_offset+x] = i_u;
920 p_dst_v[i_offset+x] = i_v;
922 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
923 p_dst_a[i_offset+x] = 0xff;
929 if( p_line->pi_underline_thickness[ i ] )
931 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
932 p_line->pi_underline_offset[ i ],
933 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
934 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
935 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
936 i_glyph_tmax, i_align_offset,
937 i_y, i_u, i_v, i_alpha,
943 /* Apply the alpha setting */
944 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
945 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
951 * This function renders a text subpicture region into another one.
952 * It also calculates the size needed for this string, and renders the
953 * needed glyphs into memory. It is used as pf_add_string callback in
954 * the vout method by this module
956 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
957 subpicture_region_t *p_region_in )
959 filter_sys_t *p_sys = p_filter->p_sys;
960 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
961 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
962 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
965 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
966 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
976 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
977 psz_string = p_region_in->psz_text;
978 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
980 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
983 if( p_region_in->p_style )
985 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
986 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
987 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
991 i_font_color = p_sys->i_font_color;
992 i_font_alpha = 255 - p_sys->i_font_opacity;
993 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
996 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
997 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
998 SetFontSize( p_filter, i_font_size );
1000 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1001 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1002 i_blue = i_font_color & 0x000000FF;
1004 result.x = result.y = 0;
1005 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1007 psz_unicode = psz_unicode_orig =
1008 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1009 if( psz_unicode == NULL )
1011 msg_Err( p_filter, "out of memory" );
1014 #if defined(WORDS_BIGENDIAN)
1015 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1017 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1019 if( iconv_handle == (vlc_iconv_t)-1 )
1021 msg_Warn( p_filter, "unable to do conversion" );
1027 const char *p_in_buffer = psz_string;
1028 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1029 i_in_bytes = strlen( psz_string );
1030 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1031 i_out_bytes_left = i_out_bytes;
1032 p_out_buffer = (char *)psz_unicode;
1033 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1034 &p_out_buffer, &i_out_bytes_left );
1036 vlc_iconv_close( iconv_handle );
1040 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1041 "bytes left %d", strerror(errno), (int)i_in_bytes );
1044 *(uint32_t*)p_out_buffer = 0;
1045 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1048 #if defined(HAVE_FRIBIDI)
1050 uint32_t *p_fribidi_string;
1051 int start_pos, pos = 0;
1053 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1054 if( !p_fribidi_string )
1056 msg_Err( p_filter, "out of memory" );
1060 /* Do bidi conversion line-by-line */
1061 while(pos < i_string_length)
1063 while(pos < i_string_length) {
1064 i_char = psz_unicode[pos];
1065 if (i_char != '\r' && i_char != '\n')
1067 p_fribidi_string[pos] = i_char;
1071 while(pos < i_string_length) {
1072 i_char = psz_unicode[pos];
1073 if (i_char == '\r' || i_char == '\n')
1077 if (pos > start_pos)
1079 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1080 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
1081 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
1085 free( psz_unicode_orig );
1086 psz_unicode = psz_unicode_orig = p_fribidi_string;
1087 p_fribidi_string[ i_string_length ] = 0;
1091 /* Calculate relative glyph positions and a bounding box for the
1093 if( !(p_line = NewLine( strlen( psz_string ))) )
1095 msg_Err( p_filter, "out of memory" );
1099 i_pen_x = i_pen_y = 0;
1101 psz_line_start = psz_unicode;
1103 #define face p_sys->p_face
1104 #define glyph face->glyph
1106 while( *psz_unicode )
1108 i_char = *psz_unicode++;
1109 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1114 if( i_char == '\n' )
1116 psz_line_start = psz_unicode;
1117 if( !(p_next = NewLine( strlen( psz_string ))) )
1119 msg_Err( p_filter, "out of memory" );
1122 p_line->p_next = p_next;
1123 p_line->i_width = line.xMax;
1124 p_line->i_height = face->size->metrics.height >> 6;
1125 p_line->pp_glyphs[ i ] = NULL;
1126 p_line->i_alpha = i_font_alpha;
1127 p_line->i_red = i_red;
1128 p_line->i_green = i_green;
1129 p_line->i_blue = i_blue;
1132 result.x = __MAX( result.x, line.xMax );
1133 result.y += face->size->metrics.height >> 6;
1136 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1137 i_pen_y += face->size->metrics.height >> 6;
1139 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1144 i_glyph_index = FT_Get_Char_Index( face, i_char );
1145 if( p_sys->i_use_kerning && i_glyph_index
1149 FT_Get_Kerning( face, i_previous, i_glyph_index,
1150 ft_kerning_default, &delta );
1151 i_pen_x += delta.x >> 6;
1154 p_line->p_glyph_pos[ i ].x = i_pen_x;
1155 p_line->p_glyph_pos[ i ].y = i_pen_y;
1156 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1159 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1163 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1166 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1170 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1171 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1174 FT_Done_Glyph( tmp_glyph );
1177 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1180 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1181 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1182 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1184 p_line->pp_glyphs[ i ] = NULL;
1186 p_line = NewLine( strlen( psz_string ));
1187 if( p_prev ) p_prev->p_next = p_line;
1188 else p_lines = p_line;
1190 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1194 if( psz_unicode == psz_line_start )
1196 msg_Warn( p_filter, "unbreakable string" );
1201 *psz_unicode = '\n';
1203 psz_unicode = psz_line_start;
1206 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1209 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1210 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1212 i_previous = i_glyph_index;
1213 i_pen_x += glyph->advance.x >> 6;
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;
1224 result.x = __MAX( result.x, line.xMax );
1225 result.y += line.yMax - line.yMin;
1230 p_region_out->i_x = p_region_in->i_x;
1231 p_region_out->i_y = p_region_in->i_y;
1233 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1234 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1236 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1238 if( psz_unicode_orig ) free( psz_unicode_orig );
1239 FreeLines( p_lines );
1243 if( psz_unicode_orig ) free( psz_unicode_orig );
1244 FreeLines( p_lines );
1245 return VLC_EGENERIC;
1248 #ifdef HAVE_FONTCONFIG
1249 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1250 uint32_t i_font_color, uint32_t i_karaoke_bg_color, vlc_bool_t b_bold,
1251 vlc_bool_t b_italic, vlc_bool_t b_uline )
1253 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1257 p_style->i_font_size = i_font_size;
1258 p_style->i_font_color = i_font_color;
1259 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1260 p_style->b_italic = b_italic;
1261 p_style->b_bold = b_bold;
1262 p_style->b_underline = b_uline;
1264 p_style->psz_fontname = strdup( psz_fontname );
1269 static void DeleteStyle( ft_style_t *p_style )
1273 if( p_style->psz_fontname )
1274 free( p_style->psz_fontname );
1279 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1286 if(( s1->i_font_size == s2->i_font_size ) &&
1287 ( s1->i_font_color == s2->i_font_color ) &&
1288 ( s1->b_italic == s2->b_italic ) &&
1289 ( s1->b_bold == s2->b_bold ) &&
1290 ( s1->b_underline == s2->b_underline ) &&
1291 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1298 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1299 uint32_t i_color, uint32_t i_karaoke_bg_color )
1301 font_stack_t *p_new;
1304 return VLC_EGENERIC;
1306 p_new = malloc( sizeof( font_stack_t ) );
1310 p_new->p_next = NULL;
1313 p_new->psz_name = strdup( psz_name );
1315 p_new->psz_name = NULL;
1317 p_new->i_size = i_size;
1318 p_new->i_color = i_color;
1319 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1327 font_stack_t *p_last;
1329 for( p_last = *p_font;
1331 p_last = p_last->p_next )
1334 p_last->p_next = p_new;
1339 static int PopFont( font_stack_t **p_font )
1341 font_stack_t *p_last, *p_next_to_last;
1343 if( !p_font || !*p_font )
1344 return VLC_EGENERIC;
1346 p_next_to_last = NULL;
1347 for( p_last = *p_font;
1349 p_last = p_last->p_next )
1351 p_next_to_last = p_last;
1354 if( p_next_to_last )
1355 p_next_to_last->p_next = NULL;
1359 free( p_last->psz_name );
1365 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1366 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1368 font_stack_t *p_last;
1370 if( !p_font || !*p_font )
1371 return VLC_EGENERIC;
1373 for( p_last=*p_font;
1375 p_last=p_last->p_next )
1378 *psz_name = p_last->psz_name;
1379 *i_size = p_last->i_size;
1380 *i_color = p_last->i_color;
1381 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1386 static void IconvText( filter_t *p_filter, const char *psz_string,
1387 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1389 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1391 /* If memory hasn't been allocated for our output string, allocate it here
1392 * - the calling function must now be responsible for freeing it.
1394 if( !*ppsz_unicode )
1395 *ppsz_unicode = (uint32_t *)
1396 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1398 /* We don't need to handle a NULL pointer in *ppsz_unicode
1399 * if we are instead testing for a non NULL value like we are here */
1403 #if defined(WORDS_BIGENDIAN)
1404 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1406 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1408 if( iconv_handle != (vlc_iconv_t)-1 )
1410 char *p_in_buffer, *p_out_buffer;
1411 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1412 i_in_bytes = strlen( psz_string );
1413 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1414 i_out_bytes_left = i_out_bytes;
1415 p_in_buffer = (char *) psz_string;
1416 p_out_buffer = (char *) *ppsz_unicode;
1417 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1418 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1420 vlc_iconv_close( iconv_handle );
1424 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1425 "bytes left %d", strerror(errno), (int)i_in_bytes );
1429 *(uint32_t*)p_out_buffer = 0;
1431 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1436 msg_Warn( p_filter, "unable to do conversion" );
1441 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1442 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1443 vlc_bool_t b_uline )
1445 ft_style_t *p_style = NULL;
1447 char *psz_fontname = NULL;
1448 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1449 uint32_t i_karaoke_bg_color = i_font_color;
1450 int i_font_size = p_sys->i_font_size;
1452 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1453 &i_font_color, &i_karaoke_bg_color ))
1455 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1456 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1461 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1462 vlc_bool_t b_uline, int i_karaoke_bgcolor,
1463 line_desc_t *p_line, uint32_t *psz_unicode,
1464 int *pi_pen_x, int i_pen_y, int *pi_start,
1465 FT_Vector *p_result )
1470 vlc_bool_t b_first_on_line = VLC_TRUE;
1473 int i_pen_x_start = *pi_pen_x;
1475 uint32_t *psz_unicode_start = psz_unicode;
1477 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1479 /* Account for part of line already in position */
1480 for( i=0; i<*pi_start; i++ )
1484 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1485 ft_glyph_bbox_pixels, &glyph_size );
1487 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1488 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1489 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1490 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1496 b_first_on_line = VLC_FALSE;
1498 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1504 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1505 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1509 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1510 ft_kerning_default, &delta );
1511 *pi_pen_x += delta.x >> 6;
1513 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1514 p_line->p_glyph_pos[ i ].y = i_pen_y;
1516 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1520 "unable to render text FT_Load_Glyph returned %d", i_error );
1521 p_line->pp_glyphs[ i ] = NULL;
1522 return VLC_EGENERIC;
1524 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1528 "unable to render text FT_Get_Glyph returned %d", i_error );
1529 p_line->pp_glyphs[ i ] = NULL;
1530 return VLC_EGENERIC;
1532 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1533 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1536 FT_Done_Glyph( tmp_glyph );
1541 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1542 p_face->size->metrics.y_scale));
1543 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1544 p_face->size->metrics.y_scale));
1546 p_line->pi_underline_offset[ i ] =
1547 ( aOffset < 0 ) ? -aOffset : aOffset;
1548 p_line->pi_underline_thickness[ i ] =
1549 ( aSize < 0 ) ? -aSize : aSize;
1551 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1552 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1553 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1554 p_line->p_fg_bg_ratio[ i ] = 0x00;
1556 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1557 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1558 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1560 while( --i > *pi_start )
1562 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1565 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1569 if( psz_unicode == psz_unicode_start )
1571 if( b_first_on_line )
1573 msg_Warn( p_filter, "unbreakable string" );
1574 p_line->pp_glyphs[ i ] = NULL;
1575 return VLC_EGENERIC;
1577 *pi_pen_x = i_pen_x_start;
1579 p_line->i_width = line.xMax;
1580 p_line->i_height = __MAX( p_line->i_height,
1581 p_face->size->metrics.height >> 6 );
1582 p_line->pp_glyphs[ i ] = NULL;
1584 p_result->x = __MAX( p_result->x, line.xMax );
1585 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1586 i_yMax - i_yMin ) );
1593 *psz_unicode = '\n';
1595 psz_unicode = psz_unicode_start;
1596 *pi_pen_x = i_pen_x_start;
1604 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1605 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1607 i_previous = i_glyph_index;
1608 *pi_pen_x += p_face->glyph->advance.x >> 6;
1611 p_line->i_width = line.xMax;
1612 p_line->i_height = __MAX( p_line->i_height,
1613 p_face->size->metrics.height >> 6 );
1614 p_line->pp_glyphs[ i ] = NULL;
1616 p_result->x = __MAX( p_result->x, line.xMax );
1617 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1618 line.yMax - line.yMin ) );
1622 /* Get rid of any text processed - if necessary repositioning
1623 * at the start of a new line of text
1627 *psz_unicode_start = '\0';
1632 for( i=0; psz_unicode[ i ]; i++ )
1633 psz_unicode_start[ i ] = psz_unicode[ i ];
1634 psz_unicode_start[ i ] = '\0';
1640 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1641 font_stack_t **p_fonts, int i_scale )
1644 char *psz_fontname = NULL;
1645 uint32_t i_font_color = 0xffffff;
1646 int i_font_alpha = 0;
1647 uint32_t i_karaoke_bg_color = 0x00ffffff;
1648 int i_font_size = 24;
1650 /* Default all attributes to the top font in the stack -- in case not
1651 * all attributes are specified in the sub-font
1653 if( VLC_SUCCESS == PeekFont( p_fonts,
1657 &i_karaoke_bg_color ))
1659 psz_fontname = strdup( psz_fontname );
1660 i_font_size = i_font_size * 1000 / i_scale;
1662 i_font_alpha = (i_font_color >> 24) & 0xff;
1663 i_font_color &= 0x00ffffff;
1665 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1667 char *psz_name = xml_ReaderName( p_xml_reader );
1668 char *psz_value = xml_ReaderValue( p_xml_reader );
1670 if( psz_name && psz_value )
1672 if( !strcasecmp( "face", psz_name ) )
1674 if( psz_fontname ) free( psz_fontname );
1675 psz_fontname = strdup( psz_value );
1677 else if( !strcasecmp( "size", psz_name ) )
1679 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1681 int i_value = atoi( psz_value );
1683 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1684 i_font_size += ( i_value * i_font_size ) / 10;
1685 else if( i_value < -5 )
1686 i_font_size = - i_value;
1687 else if( i_value > 5 )
1688 i_font_size = i_value;
1691 i_font_size = atoi( psz_value );
1693 else if( !strcasecmp( "color", psz_name ) &&
1694 ( psz_value[0] == '#' ) )
1696 i_font_color = strtol( psz_value + 1, NULL, 16 );
1697 i_font_color &= 0x00ffffff;
1699 else if( !strcasecmp( "alpha", psz_name ) &&
1700 ( psz_value[0] == '#' ) )
1702 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1703 i_font_alpha &= 0xff;
1709 rv = PushFont( p_fonts,
1711 i_font_size * i_scale / 1000,
1712 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1713 i_karaoke_bg_color );
1715 free( psz_fontname );
1720 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1721 uint32_t **psz_text_out, uint32_t *pi_runs,
1722 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1723 ft_style_t *p_style )
1725 uint32_t i_string_length = 0;
1727 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1728 *psz_text_out += i_string_length;
1730 if( ppp_styles && ppi_run_lengths )
1736 *ppp_styles = (ft_style_t **)
1737 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1739 else if( *pi_runs == 1 )
1741 *ppp_styles = (ft_style_t **)
1742 malloc( *pi_runs * sizeof( ft_style_t * ) );
1745 /* We have just malloc'ed this memory successfully -
1746 * *pi_runs HAS to be within the memory area of *ppp_styles */
1749 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1753 if( *ppi_run_lengths )
1755 *ppi_run_lengths = (uint32_t *)
1756 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1758 else if( *pi_runs == 1 )
1760 *ppi_run_lengths = (uint32_t *)
1761 malloc( *pi_runs * sizeof( uint32_t ) );
1764 /* same remarks here */
1765 if( *ppi_run_lengths )
1767 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1770 /* If we couldn't use the p_style argument due to memory allocation
1771 * problems above, release it here.
1773 if( p_style ) DeleteStyle( p_style );
1776 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1777 uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1779 /* Karaoke tags _PRECEDE_ the text they specify a duration
1780 * for, therefore we are working out the length for the
1781 * previous tag, and first time through we have nothing
1783 if( pi_k_run_lengths )
1788 /* Work out how many characters are presently in the string
1790 for( i = 0; i < i_runs; i++ )
1791 i_chars += pi_run_lengths[ i ];
1793 /* Subtract away those we've already allocated to other
1796 for( i = 0; i < i_k_runs; i++ )
1797 i_chars -= pi_k_run_lengths[ i ];
1799 pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1803 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1804 uint32_t **ppi_k_run_lengths,
1805 uint32_t **ppi_k_durations )
1807 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1809 char *psz_name = xml_ReaderName( p_xml_reader );
1810 char *psz_value = xml_ReaderValue( p_xml_reader );
1812 if( psz_name && psz_value &&
1813 !strcasecmp( "t", psz_name ) )
1815 if( ppi_k_durations && ppi_k_run_lengths )
1819 if( *ppi_k_durations )
1821 *ppi_k_durations = (uint32_t *)
1822 realloc( *ppi_k_durations,
1823 *pi_k_runs * sizeof( uint32_t ) );
1825 else if( *pi_k_runs == 1 )
1827 *ppi_k_durations = (uint32_t *)
1828 malloc( *pi_k_runs * sizeof( uint32_t ) );
1831 if( *ppi_k_run_lengths )
1833 *ppi_k_run_lengths = (uint32_t *)
1834 realloc( *ppi_k_run_lengths,
1835 *pi_k_runs * sizeof( uint32_t ) );
1837 else if( *pi_k_runs == 1 )
1839 *ppi_k_run_lengths = (uint32_t *)
1840 malloc( *pi_k_runs * sizeof( uint32_t ) );
1842 if( *ppi_k_durations )
1843 (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1845 if( *ppi_k_run_lengths )
1846 (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1849 if( psz_name ) free( psz_name );
1850 if( psz_value ) free( psz_value );
1854 static int ProcessNodes( filter_t *p_filter,
1855 xml_reader_t *p_xml_reader,
1856 text_style_t *p_font_style,
1861 uint32_t **ppi_run_lengths,
1862 ft_style_t ***ppp_styles,
1864 vlc_bool_t b_karaoke,
1865 uint32_t *pi_k_runs,
1866 uint32_t **ppi_k_run_lengths,
1867 uint32_t **ppi_k_durations )
1869 int rv = VLC_SUCCESS;
1870 filter_sys_t *p_sys = p_filter->p_sys;
1871 uint32_t *psz_text_orig = psz_text;
1872 font_stack_t *p_fonts = NULL;
1876 char *psz_node = NULL;
1878 vlc_bool_t b_italic = VLC_FALSE;
1879 vlc_bool_t b_bold = VLC_FALSE;
1880 vlc_bool_t b_uline = VLC_FALSE;
1882 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1883 i_scale = val.i_int;
1887 rv = PushFont( &p_fonts,
1888 p_font_style->psz_fontname,
1889 p_font_style->i_font_size * i_scale / 1000,
1890 (p_font_style->i_font_color & 0xffffff) |
1891 ((p_font_style->i_font_alpha & 0xff) << 24),
1892 (p_font_style->i_karaoke_background_color & 0xffffff) |
1893 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1895 if( p_font_style->i_style_flags & STYLE_BOLD )
1897 if( p_font_style->i_style_flags & STYLE_ITALIC )
1898 b_italic = VLC_TRUE;
1899 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1904 rv = PushFont( &p_fonts,
1910 if( rv != VLC_SUCCESS )
1913 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1915 switch ( xml_ReaderNodeType( p_xml_reader ) )
1917 case XML_READER_NONE:
1919 case XML_READER_ENDELEM:
1920 psz_node = xml_ReaderName( p_xml_reader );
1924 if( !strcasecmp( "font", psz_node ) )
1925 PopFont( &p_fonts );
1926 else if( !strcasecmp( "b", psz_node ) )
1928 else if( !strcasecmp( "i", psz_node ) )
1929 b_italic = VLC_FALSE;
1930 else if( !strcasecmp( "u", psz_node ) )
1931 b_uline = VLC_FALSE;
1936 case XML_READER_STARTELEM:
1937 psz_node = xml_ReaderName( p_xml_reader );
1940 if( !strcasecmp( "font", psz_node ) )
1941 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
1942 else if( !strcasecmp( "b", psz_node ) )
1944 else if( !strcasecmp( "i", psz_node ) )
1945 b_italic = VLC_TRUE;
1946 else if( !strcasecmp( "u", psz_node ) )
1948 else if( !strcasecmp( "br", psz_node ) )
1950 SetupLine( p_filter, "\n", &psz_text,
1951 pi_runs, ppi_run_lengths, ppp_styles,
1952 GetStyleFromFontStack( p_sys,
1958 else if( !strcasecmp( "k", psz_node ) )
1960 /* Only valid in karaoke */
1963 if( *pi_k_runs > 0 )
1965 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
1966 *pi_k_runs, *ppi_k_run_lengths );
1968 SetupKaraoke( p_xml_reader, pi_k_runs,
1969 ppi_k_run_lengths, ppi_k_durations );
1976 case XML_READER_TEXT:
1977 psz_node = xml_ReaderValue( p_xml_reader );
1980 /* Turn any multiple-whitespaces into single spaces */
1981 char *s = strpbrk( psz_node, "\t\r\n " );
1984 int i_whitespace = strspn( s, "\t\r\n " );
1986 if( i_whitespace > 1 )
1989 strlen( s ) - i_whitespace + 1 );
1992 s = strpbrk( s, "\t\r\n " );
1994 SetupLine( p_filter, psz_node, &psz_text,
1995 pi_runs, ppi_run_lengths, ppp_styles,
1996 GetStyleFromFontStack( p_sys,
2005 if( rv != VLC_SUCCESS )
2007 psz_text = psz_text_orig;
2013 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2014 *pi_k_runs, *ppi_k_run_lengths );
2017 *pi_len = psz_text - psz_text_orig;
2019 while( VLC_SUCCESS == PopFont( &p_fonts ) );
2024 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2028 for( k=0; k < p_sys->i_font_attachments; k++ )
2030 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
2032 FT_Face p_face = NULL;
2034 while( 0 == FT_New_Memory_Face( p_sys->p_library,
2042 vlc_bool_t match = !strcasecmp( p_face->family_name,
2043 p_style->psz_fontname );
2045 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2046 match = match && p_style->b_bold;
2048 match = match && !p_style->b_bold;
2050 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2051 match = match && p_style->b_italic;
2053 match = match && !p_style->b_italic;
2061 FT_Done_Face( p_face );
2066 return VLC_EGENERIC;
2069 static int ProcessLines( filter_t *p_filter,
2074 uint32_t *pi_run_lengths,
2075 ft_style_t **pp_styles,
2076 line_desc_t **pp_lines,
2078 FT_Vector *p_result,
2080 vlc_bool_t b_karaoke,
2082 uint32_t *pi_k_run_lengths,
2083 uint32_t *pi_k_durations )
2085 filter_sys_t *p_sys = p_filter->p_sys;
2086 ft_style_t **pp_char_styles;
2087 int *p_new_positions = NULL;
2088 int8_t *p_levels = NULL;
2089 uint8_t *pi_karaoke_bar = NULL;
2093 /* Assign each character in the text string its style explicitly, so that
2094 * after the characters have been shuffled around by Fribidi, we can re-apply
2095 * the styles, and to simplify the calculation of runs within a line.
2097 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2098 if( !pp_char_styles )
2103 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2104 /* If we can't allocate sufficient memory for karaoke, continue anyway -
2105 * we just won't be able to display the progress bar; at least we'll
2111 for( j = 0; j < i_runs; j++ )
2112 for( k = 0; k < pi_run_lengths[ j ]; k++ )
2113 pp_char_styles[ i++ ] = pp_styles[ j ];
2115 #if defined(HAVE_FRIBIDI)
2117 ft_style_t **pp_char_styles_new;
2118 int *p_old_positions;
2119 uint32_t *p_fribidi_string;
2120 int start_pos, pos = 0;
2122 pp_char_styles_new = (ft_style_t **)
2123 malloc( i_len * sizeof( ft_style_t * ));
2125 p_fribidi_string = (uint32_t *)
2126 malloc( (i_len + 1) * sizeof(uint32_t) );
2127 p_old_positions = (int *)
2128 malloc( (i_len + 1) * sizeof( int ) );
2129 p_new_positions = (int *)
2130 malloc( (i_len + 1) * sizeof( int ) );
2131 p_levels = (int8_t *)
2132 malloc( (i_len + 1) * sizeof( int8_t ) );
2134 if( ! pp_char_styles_new ||
2135 ! p_fribidi_string ||
2136 ! p_old_positions ||
2137 ! p_new_positions ||
2140 msg_Err( p_filter, "out of memory" );
2141 if( p_levels ) free( p_levels );
2142 if( p_old_positions ) free( p_old_positions );
2143 if( p_new_positions ) free( p_new_positions );
2144 if( p_fribidi_string ) free( p_fribidi_string );
2145 if( pp_char_styles_new ) free( pp_char_styles_new );
2146 if( pi_karaoke_bar ) free( pi_karaoke_bar );
2148 free( pp_char_styles );
2152 /* Do bidi conversion line-by-line */
2155 while(pos < i_len) {
2156 if (psz_text[pos] != '\n')
2158 p_fribidi_string[pos] = psz_text[pos];
2159 pp_char_styles_new[pos] = pp_char_styles[pos];
2160 p_new_positions[pos] = pos;
2165 while(pos < i_len) {
2166 if (psz_text[pos] == '\n')
2170 if (pos > start_pos)
2172 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2173 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2174 pos - start_pos, &base_dir,
2175 (FriBidiChar*)p_fribidi_string + start_pos,
2176 p_new_positions + start_pos,
2178 p_levels + start_pos );
2179 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2181 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2182 p_old_positions[ j - start_pos ] ];
2183 p_new_positions[ j ] += start_pos;
2187 free( p_old_positions );
2188 free( pp_char_styles );
2189 pp_char_styles = pp_char_styles_new;
2190 psz_text = p_fribidi_string;
2191 p_fribidi_string[ i_len ] = 0;
2194 /* Work out the karaoke */
2195 if( pi_karaoke_bar )
2197 int64_t i_last_duration = 0;
2198 int64_t i_duration = 0;
2199 int64_t i_start_pos = 0;
2200 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2202 for( k = 0; k< i_k_runs; k++ )
2204 double fraction = 0.0;
2206 i_duration += pi_k_durations[ k ];
2208 if( i_duration < i_elapsed )
2210 /* Completely finished this run-length -
2211 * let it render normally */
2215 else if( i_elapsed < i_last_duration )
2217 /* Haven't got up to this segment yet -
2218 * render it completely in karaoke BG mode */
2224 /* Partway through this run */
2226 fraction = (double)(i_elapsed - i_last_duration) /
2227 (double)pi_k_durations[ k ];
2229 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2231 double shade = pi_k_run_lengths[ k ] * fraction;
2233 if( p_new_positions )
2234 j = p_new_positions[ i_start_pos + i ];
2236 j = i_start_pos + i;
2238 if( i < (uint32_t)shade )
2239 pi_karaoke_bar[ j ] = 0xff;
2240 else if( (double)i > shade )
2241 pi_karaoke_bar[ j ] = 0x00;
2244 shade -= (int)shade;
2245 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2246 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2250 i_last_duration = i_duration;
2251 i_start_pos += pi_k_run_lengths[ k ];
2254 if( p_levels ) free( p_levels );
2255 if( p_new_positions ) free( p_new_positions );
2257 FT_Vector tmp_result;
2259 line_desc_t *p_line = NULL;
2260 line_desc_t *p_prev = NULL;
2266 p_result->x = p_result->y = 0;
2267 tmp_result.x = tmp_result.y = 0;
2270 for( k = 0; k <= (uint32_t) i_len; k++ )
2272 if( ( k == (uint32_t) i_len ) ||
2274 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2276 ft_style_t *p_style = pp_char_styles[ k - 1 ];
2278 /* End of the current style run */
2279 FT_Face p_face = NULL;
2282 /* Look for a match amongst our attachments first */
2283 CheckForEmbeddedFont( p_sys, &p_face, p_style );
2287 char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2288 p_style->psz_fontname,
2294 if( FT_New_Face( p_sys->p_library,
2295 psz_fontfile ? psz_fontfile : "", i_idx, &p_face ) )
2297 free( psz_fontfile );
2298 free( pp_char_styles );
2299 #if defined(HAVE_FRIBIDI)
2302 if( pi_karaoke_bar )
2303 free( pi_karaoke_bar );
2304 return VLC_EGENERIC;
2306 free( psz_fontfile );
2310 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2312 /* We've loaded a font face which is unhelpful for actually
2313 * rendering text - fallback to the default one.
2315 FT_Done_Face( p_face );
2319 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2320 ft_encoding_unicode ) ||
2321 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2322 p_style->i_font_size ) )
2324 if( p_face ) FT_Done_Face( p_face );
2325 free( pp_char_styles );
2326 #if defined(HAVE_FRIBIDI)
2329 if( pi_karaoke_bar )
2330 free( pi_karaoke_bar );
2331 return VLC_EGENERIC;
2333 p_sys->i_use_kerning =
2334 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2337 uint32_t *psz_unicode = (uint32_t *)
2338 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2341 msg_Err( p_filter, "out of memory" );
2342 if( p_face ) FT_Done_Face( p_face );
2343 free( pp_char_styles );
2344 free( psz_unicode );
2345 #if defined(HAVE_FRIBIDI)
2348 if( pi_karaoke_bar )
2349 free( pi_karaoke_bar );
2352 memcpy( psz_unicode, psz_text + i_prev,
2353 sizeof( uint32_t ) * ( k - i_prev ) );
2354 psz_unicode[ k - i_prev ] = 0;
2355 while( *psz_unicode )
2359 if( !(p_line = NewLine( i_len - i_prev)) )
2361 msg_Err( p_filter, "out of memory" );
2362 if( p_face ) FT_Done_Face( p_face );
2363 free( pp_char_styles );
2364 free( psz_unicode );
2365 #if defined(HAVE_FRIBIDI)
2368 if( pi_karaoke_bar )
2369 free( pi_karaoke_bar );
2372 /* New Color mode only works in YUVA rendering mode --
2373 * (RGB mode has palette constraints on it). We therefore
2374 * need to populate the legacy colour fields also.
2376 p_line->b_new_color_mode = VLC_TRUE;
2377 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2378 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2379 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2380 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2381 p_line->p_next = NULL;
2383 i_pen_y += tmp_result.y;
2387 if( p_prev ) p_prev->p_next = p_line;
2388 else *pp_lines = p_line;
2391 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2392 p_style->i_font_color, p_style->b_underline,
2393 p_style->i_karaoke_bg_color,
2394 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2395 &tmp_result ) != VLC_SUCCESS )
2397 if( p_face ) FT_Done_Face( p_face );
2398 free( pp_char_styles );
2399 free( psz_unicode );
2400 #if defined(HAVE_FRIBIDI)
2403 if( pi_karaoke_bar )
2404 free( pi_karaoke_bar );
2405 return VLC_EGENERIC;
2410 p_result->x = __MAX( p_result->x, tmp_result.x );
2411 p_result->y += tmp_result.y;
2417 free( psz_unicode );
2418 if( p_face ) FT_Done_Face( p_face );
2422 free( pp_char_styles );
2423 #if defined(HAVE_FRIBIDI)
2428 p_result->x = __MAX( p_result->x, tmp_result.x );
2429 p_result->y += tmp_result.y;
2432 if( pi_karaoke_bar )
2435 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2437 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2439 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2443 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2445 /* 100% BG colour will render faster if we
2446 * instead make it 100% FG colour, so leave
2447 * the ratio alone and copy the value across
2449 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2453 if( pi_karaoke_bar[ i ] & 0x80 )
2455 /* Swap Left and Right sides over for Right aligned
2456 * language text (eg. Arabic, Hebrew)
2458 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2460 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2461 p_line->p_bg_rgb[ k ] = i_tmp;
2463 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2466 /* Jump over the '\n' at the line-end */
2469 free( pi_karaoke_bar );
2475 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2476 subpicture_region_t *p_region_in )
2478 int rv = VLC_SUCCESS;
2479 stream_t *p_sub = NULL;
2480 xml_t *p_xml = NULL;
2481 xml_reader_t *p_xml_reader = NULL;
2483 if( !p_region_in || !p_region_in->psz_html )
2484 return VLC_EGENERIC;
2486 /* Reset the default fontsize in case screen metrics have changed */
2487 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2489 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2490 (uint8_t *) p_region_in->psz_html,
2491 strlen( p_region_in->psz_html ),
2495 p_xml = xml_Create( p_filter );
2498 vlc_bool_t b_karaoke = VLC_FALSE;
2500 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2503 /* Look for Root Node */
2504 if( xml_ReaderRead( p_xml_reader ) == 1 )
2506 char *psz_node = xml_ReaderName( p_xml_reader );
2508 if( !strcasecmp( "karaoke", psz_node ) )
2510 /* We're going to have to render the text a number
2511 * of times to show the progress marker on the text.
2513 var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2514 b_karaoke = VLC_TRUE;
2516 else if( !strcasecmp( "text", psz_node ) )
2518 b_karaoke = VLC_FALSE;
2522 /* Only text and karaoke tags are supported */
2523 xml_ReaderDelete( p_xml, p_xml_reader );
2524 p_xml_reader = NULL;
2536 uint32_t i_runs = 0;
2537 uint32_t i_k_runs = 0;
2538 uint32_t *pi_run_lengths = NULL;
2539 uint32_t *pi_k_run_lengths = NULL;
2540 uint32_t *pi_k_durations = NULL;
2541 ft_style_t **pp_styles = NULL;
2543 line_desc_t *p_lines = NULL;
2545 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2546 sizeof( uint32_t ) );
2551 rv = ProcessNodes( p_filter, p_xml_reader,
2552 p_region_in->p_style, psz_text, &i_len,
2553 &i_runs, &pi_run_lengths, &pp_styles,
2554 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2557 p_region_out->i_x = p_region_in->i_x;
2558 p_region_out->i_y = p_region_in->i_y;
2560 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2562 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2563 pi_run_lengths, pp_styles, &p_lines, &result,
2564 b_karaoke, i_k_runs, pi_k_run_lengths,
2568 for( k=0; k<i_runs; k++)
2569 DeleteStyle( pp_styles[k] );
2571 free( pi_run_lengths );
2574 /* Don't attempt to render text that couldn't be layed out
2577 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2579 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2581 Render( p_filter, p_region_out, p_lines,
2582 result.x, result.y );
2586 RenderYUVA( p_filter, p_region_out, p_lines,
2587 result.x, result.y );
2591 FreeLines( p_lines );
2593 xml_ReaderDelete( p_xml, p_xml_reader );
2595 xml_Delete( p_xml );
2597 stream_Delete( p_sub );
2603 static char* FontConfig_Select( FcConfig* priv, const char* family,
2604 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2607 FcPattern *pat, *p_pat;
2611 pat = FcPatternCreate();
2612 if (!pat) return NULL;
2614 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2615 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2616 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2617 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2619 FcDefaultSubstitute( pat );
2621 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2623 FcPatternDestroy( pat );
2627 p_pat = FcFontMatch( priv, pat, &result );
2628 FcPatternDestroy( pat );
2629 if( !p_pat ) return NULL;
2631 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2632 || ( val_b != FcTrue ) )
2634 FcPatternDestroy( p_pat );
2637 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2642 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2644 FcPatternDestroy( p_pat );
2649 if( strcasecmp((const char*)val_s, family ) != 0 )
2650 msg_Warn( p_filter, "fontconfig: selected font family is not"
2651 "the requested one: '%s' != '%s'\n",
2652 (const char*)val_s, family );
2655 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2657 FcPatternDestroy( p_pat );
2661 FcPatternDestroy( p_pat );
2662 return strdup( (const char*)val_s );
2666 static void FreeLine( line_desc_t *p_line )
2669 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2671 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2673 free( p_line->pp_glyphs );
2674 free( p_line->p_glyph_pos );
2675 free( p_line->p_fg_rgb );
2676 free( p_line->p_bg_rgb );
2677 free( p_line->p_fg_bg_ratio );
2678 free( p_line->pi_underline_offset );
2679 free( p_line->pi_underline_thickness );
2683 static void FreeLines( line_desc_t *p_lines )
2685 line_desc_t *p_line, *p_next;
2687 if( !p_lines ) return;
2689 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2691 p_next = p_line->p_next;
2696 static line_desc_t *NewLine( int i_count )
2698 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2700 if( !p_line ) return NULL;
2701 p_line->i_height = 0;
2702 p_line->i_width = 0;
2703 p_line->p_next = NULL;
2705 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2706 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2707 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2708 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2709 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2710 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2711 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2712 if( ( p_line->pp_glyphs == NULL ) ||
2713 ( p_line->p_glyph_pos == NULL ) ||
2714 ( p_line->p_fg_rgb == NULL ) ||
2715 ( p_line->p_bg_rgb == NULL ) ||
2716 ( p_line->p_fg_bg_ratio == NULL ) ||
2717 ( p_line->pi_underline_offset == NULL ) ||
2718 ( p_line->pi_underline_thickness == NULL ) )
2720 if( p_line->pi_underline_thickness )
2721 free( p_line->pi_underline_thickness );
2722 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2723 if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2724 if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2725 if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2726 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2727 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2731 p_line->pp_glyphs[0] = NULL;
2732 p_line->b_new_color_mode = VLC_FALSE;
2737 static int GetFontSize( filter_t *p_filter )
2739 filter_sys_t *p_sys = p_filter->p_sys;
2743 if( p_sys->i_default_font_size )
2745 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2746 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2748 i_size = p_sys->i_default_font_size;
2752 var_Get( p_filter, "freetype-rel-fontsize", &val );
2753 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2754 p_filter->p_sys->i_display_height =
2755 p_filter->fmt_out.video.i_height;
2759 msg_Warn( p_filter, "invalid fontsize, using 12" );
2760 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2761 i_size = 12 * val.i_int / 1000;
2768 static int SetFontSize( filter_t *p_filter, int i_size )
2770 filter_sys_t *p_sys = p_filter->p_sys;
2774 i_size = GetFontSize( p_filter );
2776 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2779 p_sys->i_font_size = i_size;
2781 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2783 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2784 return VLC_EGENERIC;
2790 static void YUVFromRGB( uint32_t i_argb,
2791 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2793 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2794 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2795 int i_blue = ( i_argb & 0x000000ff );
2797 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2798 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2799 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2800 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2801 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2802 -585 * i_blue + 4096 + 1048576) >> 13, 240);