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 <b dot purcell at adbglobal dot com>
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 *****************************************************************************/
29 #include <stdlib.h> /* malloc(), free() */
32 #ifdef HAVE_LINUX_LIMITS_H
33 # include <linux/limits.h>
39 #include <vlc_block.h>
40 #include <vlc_filter.h>
41 #include <vlc_stream.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 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
88 static int RenderText( filter_t *, subpicture_region_t *,
89 subpicture_region_t * );
90 #ifdef HAVE_FONTCONFIG
91 static int RenderHtml( filter_t *, subpicture_region_t *,
92 subpicture_region_t * );
93 static char *FontConfig_Select( FcConfig *, const char *,
94 vlc_bool_t, vlc_bool_t, int * );
96 static line_desc_t *NewLine( byte_t * );
98 static int SetFontSize( filter_t *, int );
100 /*****************************************************************************
102 *****************************************************************************/
103 #define FONT_TEXT N_("Font")
104 #define FONT_LONGTEXT N_("Filename for the font you want to use")
105 #define FONTSIZE_TEXT N_("Font size in pixels")
106 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
107 "that will be rendered on the video. " \
108 "If set to something different than 0 this option will override the " \
109 "relative font size." )
110 #define OPACITY_TEXT N_("Opacity")
111 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
112 "text that will be rendered on the video. 0 = transparent, " \
113 "255 = totally opaque. " )
114 #define COLOR_TEXT N_("Text default color")
115 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
116 "the video. This must be an hexadecimal (like HTML colors). The first two "\
117 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
118 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
119 #define FONTSIZER_TEXT N_("Relative font size")
120 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
121 "fonts that will be rendered on the video. If absolute font size is set, "\
122 "relative size will be overriden." )
124 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
125 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
126 N_("Large"), N_("Larger") };
127 #define YUVP_TEXT N_("Use YUVP renderer")
128 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
129 "This option is only needed if you want to encode into DVB subtitles" )
130 #define EFFECT_TEXT N_("Font Effect")
131 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
132 "text to improve its readability." )
134 #define EFFECT_BACKGROUND 1
135 #define EFFECT_OUTLINE 2
136 #define EFFECT_OUTLINE_FAT 3
138 static int pi_effects[] = { 1, 2, 3 };
139 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
141 static int pi_color_values[] = {
142 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
143 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
144 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
146 static const char *ppsz_color_descriptions[] = {
147 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
148 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
149 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
152 set_shortname( _("Text renderer"));
153 set_description( _("Freetype2 font renderer") );
154 set_category( CAT_VIDEO );
155 set_subcategory( SUBCAT_VIDEO_SUBPIC );
157 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
160 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
161 FONTSIZE_LONGTEXT, VLC_TRUE );
163 /* opacity valid on 0..255, with default 255 = fully opaque */
164 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
165 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
167 /* hook to the color values list, with default 0x00ffffff = white */
168 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
169 COLOR_LONGTEXT, VLC_FALSE );
170 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
172 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
173 FONTSIZER_LONGTEXT, VLC_FALSE );
174 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
175 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
176 EFFECT_LONGTEXT, VLC_FALSE );
177 change_integer_list( pi_effects, ppsz_effects_text, 0 );
179 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
180 YUVP_LONGTEXT, VLC_TRUE );
181 set_capability( "text renderer", 100 );
182 add_shortcut( "text" );
183 set_callbacks( Create, Destroy );
188 /** NULL-terminated list of glyphs making the string */
189 FT_BitmapGlyph *pp_glyphs;
190 /** list of relative positions for the glyphs */
191 FT_Vector *p_glyph_pos;
192 /** list of RGB information for styled text
193 * -- if the rendering mode supports it (RenderYUVA) and
194 * b_new_color_mode is set, then it becomes possible to
195 * have multicoloured text within the subtitles. */
197 vlc_bool_t b_new_color_mode;
198 /** underline information -- only supplied if text should be underlined */
199 uint16_t *pi_underline_offset;
200 uint16_t *pi_underline_thickness;
204 int i_red, i_green, i_blue;
210 typedef struct font_stack_t font_stack_t;
218 font_stack_t *p_next;
221 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
222 static void FreeLines( line_desc_t * );
223 static void FreeLine( line_desc_t * );
225 /*****************************************************************************
226 * filter_sys_t: freetype local data
227 *****************************************************************************
228 * This structure is part of the video output thread descriptor.
229 * It describes the freetype specific properties of an output thread.
230 *****************************************************************************/
233 FT_Library p_library; /* handle to library */
234 FT_Face p_face; /* handle to face object */
235 vlc_bool_t i_use_kerning;
236 uint8_t i_font_opacity;
241 int i_default_font_size;
242 int i_display_height;
243 #ifdef HAVE_FONTCONFIG
244 FcConfig *p_fontconfig;
248 /*****************************************************************************
249 * Create: allocates osd-text video thread output method
250 *****************************************************************************
251 * This function allocates and initializes a Clone vout method.
252 *****************************************************************************/
253 static int Create( vlc_object_t *p_this )
255 filter_t *p_filter = (filter_t *)p_this;
257 char *psz_fontfile = NULL;
261 /* Allocate structure */
262 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
265 msg_Err( p_filter, "out of memory" );
269 p_sys->p_library = 0;
270 p_sys->i_font_size = 0;
271 p_sys->i_display_height = 0;
273 var_Create( p_filter, "freetype-font",
274 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
275 var_Create( p_filter, "freetype-fontsize",
276 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
277 var_Create( p_filter, "freetype-rel-fontsize",
278 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
279 var_Create( p_filter, "freetype-opacity",
280 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
281 var_Create( p_filter, "freetype-effect",
282 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
283 var_Get( p_filter, "freetype-opacity", &val );
284 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
285 var_Create( p_filter, "freetype-color",
286 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
287 var_Get( p_filter, "freetype-color", &val );
288 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
289 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
291 /* Look what method was requested */
292 var_Get( p_filter, "freetype-font", &val );
293 psz_fontfile = val.psz_string;
294 if( !psz_fontfile || !*psz_fontfile )
296 if( psz_fontfile ) free( psz_fontfile );
297 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
299 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
300 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
301 #elif defined(__APPLE__)
302 strcpy( psz_fontfile, DEFAULT_FONT );
304 msg_Err( p_filter, "user didn't specify a font" );
309 #ifdef HAVE_FONTCONFIG
311 p_sys->p_fontconfig = FcConfigGetCurrent();
313 p_sys->p_fontconfig = NULL;
315 i_error = FT_Init_FreeType( &p_sys->p_library );
318 msg_Err( p_filter, "couldn't initialize freetype" );
322 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
324 if( i_error == FT_Err_Unknown_File_Format )
326 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
331 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
335 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
338 msg_Err( p_filter, "font has no unicode translation table" );
342 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
344 var_Get( p_filter, "freetype-fontsize", &val );
345 p_sys->i_default_font_size = val.i_int;
346 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
348 if( psz_fontfile ) free( psz_fontfile );
349 p_filter->pf_render_text = RenderText;
350 #ifdef HAVE_FONTCONFIG
351 p_filter->pf_render_html = RenderHtml;
353 p_filter->pf_render_html = NULL;
358 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
359 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
360 if( psz_fontfile ) free( psz_fontfile );
365 /*****************************************************************************
366 * Destroy: destroy Clone video thread output method
367 *****************************************************************************
368 * Clean up all data and library connections
369 *****************************************************************************/
370 static void Destroy( vlc_object_t *p_this )
372 filter_t *p_filter = (filter_t *)p_this;
373 filter_sys_t *p_sys = p_filter->p_sys;
375 #ifdef HAVE_FONTCONFIG
376 FcConfigDestroy( p_sys->p_fontconfig );
377 p_sys->p_fontconfig = NULL;
378 /* FcFini asserts calling the subfunction FcCacheFini()
379 * even if no other library functions have been made since FcInit(),
380 * so don't call it. */
382 FT_Done_Face( p_sys->p_face );
383 FT_Done_FreeType( p_sys->p_library );
387 /*****************************************************************************
388 * Render: place string in picture
389 *****************************************************************************
390 * This function merges the previously rendered freetype glyphs into a picture
391 *****************************************************************************/
392 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
393 line_desc_t *p_line, int i_width, int i_height )
395 static uint8_t pi_gamma[16] =
396 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
397 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
401 int i, x, y, i_pitch;
402 uint8_t i_y; /* YUV values, derived from incoming RGB */
404 subpicture_region_t *p_region_tmp;
406 /* Create a new subpicture region */
407 memset( &fmt, 0, sizeof(video_format_t) );
408 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
410 fmt.i_width = fmt.i_visible_width = i_width + 4;
411 fmt.i_height = fmt.i_visible_height = i_height + 4;
412 if( p_region->fmt.i_visible_width > 0 )
413 fmt.i_visible_width = p_region->fmt.i_visible_width;
414 if( p_region->fmt.i_visible_height > 0 )
415 fmt.i_visible_height = p_region->fmt.i_visible_height;
416 fmt.i_x_offset = fmt.i_y_offset = 0;
417 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
420 msg_Err( p_filter, "cannot allocate SPU region" );
424 p_region->fmt = p_region_tmp->fmt;
425 p_region->picture = p_region_tmp->picture;
426 free( p_region_tmp );
428 /* Calculate text color components */
429 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
430 25 * p_line->i_blue + 128) >> 8) + 16;
431 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
432 112 * p_line->i_blue + 128) >> 8) + 128;
433 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
434 18 * p_line->i_blue + 128) >> 8) + 128;
437 fmt.p_palette->i_entries = 16;
438 for( i = 0; i < 8; i++ )
440 fmt.p_palette->palette[i][0] = 0;
441 fmt.p_palette->palette[i][1] = 0x80;
442 fmt.p_palette->palette[i][2] = 0x80;
443 fmt.p_palette->palette[i][3] = pi_gamma[i];
444 fmt.p_palette->palette[i][3] =
445 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
447 for( i = 8; i < fmt.p_palette->i_entries; i++ )
449 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
450 fmt.p_palette->palette[i][1] = i_u;
451 fmt.p_palette->palette[i][2] = i_v;
452 fmt.p_palette->palette[i][3] = pi_gamma[i];
453 fmt.p_palette->palette[i][3] =
454 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
457 p_dst = p_region->picture.Y_PIXELS;
458 i_pitch = p_region->picture.Y_PITCH;
460 /* Initialize the region pixels */
461 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
463 for( ; p_line != NULL; p_line = p_line->p_next )
465 int i_glyph_tmax = 0;
466 int i_bitmap_offset, i_offset, i_align_offset = 0;
467 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
469 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
470 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
473 if( p_line->i_width < i_width )
475 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
476 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
478 i_align_offset = i_width - p_line->i_width;
480 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
481 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
483 i_align_offset = ( i_width - p_line->i_width ) / 2;
487 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
489 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
491 i_offset = ( p_line->p_glyph_pos[ i ].y +
492 i_glyph_tmax - p_glyph->top + 2 ) *
493 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
496 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
498 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
500 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
502 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
509 /* Outlining (find something better than nearest neighbour filtering ?) */
512 uint8_t *p_dst = p_region->picture.Y_PIXELS;
513 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
514 uint8_t left, current;
516 for( y = 1; y < (int)fmt.i_height - 1; y++ )
518 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
519 p_dst += p_region->picture.Y_PITCH;
522 for( x = 1; x < (int)fmt.i_width - 1; x++ )
525 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
526 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;
530 memset( p_top, 0, fmt.i_width );
536 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
537 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
538 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
539 int i_glyph_tmax, int i_align_offset,
540 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
541 subpicture_region_t *p_region)
545 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
547 p_dst_y = p_region->picture.Y_PIXELS;
548 p_dst_u = p_region->picture.U_PIXELS;
549 p_dst_v = p_region->picture.V_PIXELS;
550 p_dst_a = p_region->picture.A_PIXELS;
551 i_pitch = p_region->picture.A_PITCH;
553 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
554 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
556 for( y = 0; y < i_line_thickness; y++ )
558 int i_extra = p_this_glyph->bitmap.width;
562 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
563 (p_this_glyph_pos->x + p_this_glyph->left);
565 for( x = 0; x < i_extra; x++ )
567 vlc_bool_t b_ok = VLC_TRUE;
569 /* break the underline around the tails of any glyphs which cross it */
570 for( z = x - i_line_thickness;
571 z < x + i_line_thickness && b_ok;
574 if( p_next_glyph && ( z >= i_extra ) )
576 int i_row = i_line_offset + p_next_glyph->top + y;
578 if( ( p_next_glyph->bitmap.rows > i_row ) &&
579 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
584 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
586 int i_row = i_line_offset + p_this_glyph->top + y;
588 if( ( p_this_glyph->bitmap.rows > i_row ) &&
589 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
598 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
599 p_dst_u[i_offset+x] = i_u;
600 p_dst_v[i_offset+x] = i_v;
601 p_dst_a[i_offset+x] = 255;
608 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
610 uint8_t *p_dst = p_region->picture.A_PIXELS;
611 int i_pitch = p_region->picture.A_PITCH;
614 for( ; p_line != NULL; p_line = p_line->p_next )
616 int i_glyph_tmax=0, i = 0;
617 int i_bitmap_offset, i_offset, i_align_offset = 0;
618 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
620 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
621 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
624 if( p_line->i_width < i_width )
626 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
627 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
629 i_align_offset = i_width - p_line->i_width;
631 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
632 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
634 i_align_offset = ( i_width - p_line->i_width ) / 2;
638 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
640 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
642 i_offset = ( p_line->p_glyph_pos[ i ].y +
643 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
644 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
645 i_align_offset +xoffset;
647 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
649 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
651 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
652 if( p_dst[i_offset+x] <
653 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
655 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
664 /*****************************************************************************
665 * Render: place string in picture
666 *****************************************************************************
667 * This function merges the previously rendered freetype glyphs into a picture
668 *****************************************************************************/
669 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
670 line_desc_t *p_line, int i_width, int i_height )
672 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
674 int i, x, y, i_pitch, i_alpha;
675 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
676 subpicture_region_t *p_region_tmp;
678 if( i_width == 0 || i_height == 0 )
681 /* Create a new subpicture region */
682 memset( &fmt, 0, sizeof(video_format_t) );
683 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
685 fmt.i_width = fmt.i_visible_width = i_width + 6;
686 fmt.i_height = fmt.i_visible_height = i_height + 6;
687 if( p_region->fmt.i_visible_width > 0 )
688 fmt.i_visible_width = p_region->fmt.i_visible_width;
689 if( p_region->fmt.i_visible_height > 0 )
690 fmt.i_visible_height = p_region->fmt.i_visible_height;
691 fmt.i_x_offset = fmt.i_y_offset = 0;
692 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
695 msg_Err( p_filter, "cannot allocate SPU region" );
699 p_region->fmt = p_region_tmp->fmt;
700 p_region->picture = p_region_tmp->picture;
701 free( p_region_tmp );
703 /* Calculate text color components */
704 i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red + 4130 * p_line->i_green +
705 802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
706 i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red + -2384 * p_line->i_green +
707 3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
708 i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
709 -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
710 i_alpha = p_line->i_alpha;
712 p_dst_y = p_region->picture.Y_PIXELS;
713 p_dst_u = p_region->picture.U_PIXELS;
714 p_dst_v = p_region->picture.V_PIXELS;
715 p_dst_a = p_region->picture.A_PIXELS;
716 i_pitch = p_region->picture.A_PITCH;
718 /* Initialize the region pixels */
719 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
721 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
722 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
723 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
724 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
728 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
729 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
730 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
731 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
733 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
734 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
736 DrawBlack( p_line, i_width, p_region, 0, 0);
737 DrawBlack( p_line, i_width, p_region, -1, 0);
738 DrawBlack( p_line, i_width, p_region, 0, -1);
739 DrawBlack( p_line, i_width, p_region, 1, 0);
740 DrawBlack( p_line, i_width, p_region, 0, 1);
743 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
745 DrawBlack( p_line, i_width, p_region, -1, -1);
746 DrawBlack( p_line, i_width, p_region, -1, 1);
747 DrawBlack( p_line, i_width, p_region, 1, -1);
748 DrawBlack( p_line, i_width, p_region, 1, 1);
750 DrawBlack( p_line, i_width, p_region, -2, 0);
751 DrawBlack( p_line, i_width, p_region, 0, -2);
752 DrawBlack( p_line, i_width, p_region, 2, 0);
753 DrawBlack( p_line, i_width, p_region, 0, 2);
755 DrawBlack( p_line, i_width, p_region, -2, -2);
756 DrawBlack( p_line, i_width, p_region, -2, 2);
757 DrawBlack( p_line, i_width, p_region, 2, -2);
758 DrawBlack( p_line, i_width, p_region, 2, 2);
760 DrawBlack( p_line, i_width, p_region, -3, 0);
761 DrawBlack( p_line, i_width, p_region, 0, -3);
762 DrawBlack( p_line, i_width, p_region, 3, 0);
763 DrawBlack( p_line, i_width, p_region, 0, 3);
766 for( ; p_line != NULL; p_line = p_line->p_next )
768 int i_glyph_tmax = 0;
769 int i_bitmap_offset, i_offset, i_align_offset = 0;
770 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
772 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
773 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
776 if( p_line->i_width < i_width )
778 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
779 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
781 i_align_offset = i_width - p_line->i_width;
783 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
784 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
786 i_align_offset = ( i_width - p_line->i_width ) / 2;
790 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
792 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
794 i_offset = ( p_line->p_glyph_pos[ i ].y +
795 i_glyph_tmax - p_glyph->top + 3 ) *
796 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
799 if( p_line->b_new_color_mode )
801 /* Every glyph can (and in fact must) have its own color */
802 int i_red = ( p_line->p_rgb[ i ] & 0x00ff0000 ) >> 16;
803 int i_green = ( p_line->p_rgb[ i ] & 0x0000ff00 ) >> 8;
804 int i_blue = ( p_line->p_rgb[ i ] & 0x000000ff );
806 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
807 802 * i_blue + 4096 + 131072 ) >> 13, 235);
808 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
809 3598 * i_blue + 4096 + 1048576) >> 13, 240);
810 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
811 -585 * i_blue + 4096 + 1048576) >> 13, 240);
814 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
816 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
818 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
820 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
821 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
823 p_dst_u[i_offset+x] = i_u;
824 p_dst_v[i_offset+x] = i_v;
826 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
827 p_dst_a[i_offset+x] = 0xff;
833 if( p_line->pi_underline_thickness[ i ] )
835 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
836 p_line->pi_underline_offset[ i ],
837 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
838 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
839 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
840 i_glyph_tmax, i_align_offset,
841 i_y, i_u, i_v, i_alpha,
847 /* Apply the alpha setting */
848 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
849 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
855 * This function renders a text subpicture region into another one.
856 * It also calculates the size needed for this string, and renders the
857 * needed glyphs into memory. It is used as pf_add_string callback in
858 * the vout method by this module
860 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
861 subpicture_region_t *p_region_in )
863 filter_sys_t *p_sys = p_filter->p_sys;
864 line_desc_t *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
865 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
866 uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
869 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
870 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
878 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
879 psz_string = p_region_in->psz_text;
880 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
882 if( p_region_in->p_style )
884 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
885 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
886 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
890 i_font_color = p_sys->i_font_color;
891 i_font_alpha = 255 - p_sys->i_font_opacity;
892 i_font_size = p_sys->i_default_font_size;
895 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
896 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
897 SetFontSize( p_filter, i_font_size );
899 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
900 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
901 i_blue = i_font_color & 0x000000FF;
903 result.x = result.y = 0;
904 line.xMin = line.xMax = line.yMin = line.yMax = 0;
906 psz_unicode = psz_unicode_orig =
907 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
908 if( psz_unicode == NULL )
910 msg_Err( p_filter, "out of memory" );
913 #if defined(WORDS_BIGENDIAN)
914 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
916 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
918 if( iconv_handle == (vlc_iconv_t)-1 )
920 msg_Warn( p_filter, "unable to do conversion" );
926 const char *p_in_buffer = psz_string;
927 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
928 i_in_bytes = strlen( psz_string );
929 i_out_bytes = i_in_bytes * sizeof( uint32_t );
930 i_out_bytes_left = i_out_bytes;
931 p_out_buffer = (char *)psz_unicode;
932 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
933 &p_out_buffer, &i_out_bytes_left );
935 vlc_iconv_close( iconv_handle );
939 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
940 "bytes left %d", strerror(errno), (int)i_in_bytes );
943 *(uint32_t*)p_out_buffer = 0;
944 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
947 #if defined(HAVE_FRIBIDI)
949 uint32_t *p_fribidi_string;
950 int start_pos, pos = 0;
952 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
954 /* Do bidi conversion line-by-line */
955 while(pos < i_string_length)
957 while(pos < i_string_length) {
958 i_char = psz_unicode[pos];
959 if (i_char != '\r' && i_char != '\n')
961 p_fribidi_string[pos] = i_char;
965 while(pos < i_string_length) {
966 i_char = psz_unicode[pos];
967 if (i_char == '\r' || i_char == '\n')
973 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
974 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
975 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
979 free( psz_unicode_orig );
980 psz_unicode = psz_unicode_orig = p_fribidi_string;
981 p_fribidi_string[ i_string_length ] = 0;
985 /* Calculate relative glyph positions and a bounding box for the
987 if( !(p_line = NewLine( (byte_t *)psz_string )) )
989 msg_Err( p_filter, "out of memory" );
993 i_pen_x = i_pen_y = 0;
995 psz_line_start = psz_unicode;
997 #define face p_sys->p_face
998 #define glyph face->glyph
1000 while( *psz_unicode )
1002 i_char = *psz_unicode++;
1003 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1008 if( i_char == '\n' )
1010 psz_line_start = psz_unicode;
1011 if( !(p_next = NewLine( (byte_t *)psz_string )) )
1013 msg_Err( p_filter, "out of memory" );
1016 p_line->p_next = p_next;
1017 p_line->i_width = line.xMax;
1018 p_line->i_height = face->size->metrics.height >> 6;
1019 p_line->pp_glyphs[ i ] = NULL;
1020 p_line->i_alpha = i_font_alpha;
1021 p_line->i_red = i_red;
1022 p_line->i_green = i_green;
1023 p_line->i_blue = i_blue;
1026 result.x = __MAX( result.x, line.xMax );
1027 result.y += face->size->metrics.height >> 6;
1030 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1031 i_pen_y += face->size->metrics.height >> 6;
1033 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1038 i_glyph_index = FT_Get_Char_Index( face, i_char );
1039 if( p_sys->i_use_kerning && i_glyph_index
1043 FT_Get_Kerning( face, i_previous, i_glyph_index,
1044 ft_kerning_default, &delta );
1045 i_pen_x += delta.x >> 6;
1048 p_line->p_glyph_pos[ i ].x = i_pen_x;
1049 p_line->p_glyph_pos[ i ].y = i_pen_y;
1050 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1053 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1057 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1060 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1064 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1065 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1068 FT_Done_Glyph( tmp_glyph );
1071 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1074 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1075 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1076 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1078 p_line->pp_glyphs[ i ] = NULL;
1080 p_line = NewLine( (byte_t *)psz_string );
1081 if( p_prev ) p_prev->p_next = p_line;
1082 else p_lines = p_line;
1084 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1088 if( psz_unicode == psz_line_start )
1090 msg_Warn( p_filter, "unbreakable string" );
1095 *psz_unicode = '\n';
1097 psz_unicode = psz_line_start;
1100 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1103 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1104 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1106 i_previous = i_glyph_index;
1107 i_pen_x += glyph->advance.x >> 6;
1111 p_line->i_width = line.xMax;
1112 p_line->i_height = face->size->metrics.height >> 6;
1113 p_line->pp_glyphs[ i ] = NULL;
1114 p_line->i_alpha = i_font_alpha;
1115 p_line->i_red = i_red;
1116 p_line->i_green = i_green;
1117 p_line->i_blue = i_blue;
1118 result.x = __MAX( result.x, line.xMax );
1119 result.y += line.yMax - line.yMin;
1124 p_region_out->i_x = p_region_in->i_x;
1125 p_region_out->i_y = p_region_in->i_y;
1127 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1128 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1130 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1132 if( psz_unicode_orig ) free( psz_unicode_orig );
1133 FreeLines( p_lines );
1137 if( psz_unicode_orig ) free( psz_unicode_orig );
1138 FreeLines( p_lines );
1139 return VLC_EGENERIC;
1142 #ifdef HAVE_FONTCONFIG
1143 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1144 int i_color, int i_alpha )
1146 font_stack_t *p_new;
1149 return VLC_EGENERIC;
1151 p_new = malloc( sizeof( font_stack_t ) );
1152 p_new->p_next = NULL;
1155 p_new->psz_name = strdup( psz_name );
1157 p_new->psz_name = NULL;
1159 p_new->i_size = i_size;
1160 p_new->i_color = i_color;
1161 p_new->i_alpha = i_alpha;
1169 font_stack_t *p_last;
1171 for( p_last = *p_font;
1173 p_last = p_last->p_next )
1176 p_last->p_next = p_new;
1181 static int PopFont( font_stack_t **p_font )
1183 font_stack_t *p_last, *p_next_to_last;
1185 if( !p_font || !*p_font )
1186 return VLC_EGENERIC;
1188 p_next_to_last = NULL;
1189 for( p_last = *p_font;
1191 p_last = p_last->p_next )
1193 p_next_to_last = p_last;
1196 if( p_next_to_last )
1197 p_next_to_last->p_next = NULL;
1201 free( p_last->psz_name );
1207 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1208 int *i_color, int *i_alpha )
1210 font_stack_t *p_last;
1212 if( !p_font || !*p_font )
1213 return VLC_EGENERIC;
1215 for( p_last=*p_font;
1217 p_last=p_last->p_next )
1220 *psz_name = p_last->psz_name;
1221 *i_size = p_last->i_size;
1222 *i_color = p_last->i_color;
1223 *i_alpha = p_last->i_alpha;
1228 static uint32_t *IconvText( filter_t *p_filter, char *psz_string )
1230 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1231 uint32_t *psz_unicode;
1232 int i_string_length;
1235 malloc( ( strlen( psz_string ) + 1 ) * sizeof( uint32_t ) );
1236 if( psz_unicode == NULL )
1238 msg_Err( p_filter, "out of memory" );
1241 #if defined(WORDS_BIGENDIAN)
1242 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1244 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1246 if( iconv_handle == (vlc_iconv_t)-1 )
1248 msg_Warn( p_filter, "unable to do conversion" );
1249 free( psz_unicode );
1254 char *p_in_buffer, *p_out_buffer;
1255 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1256 i_in_bytes = strlen( psz_string );
1257 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1258 i_out_bytes_left = i_out_bytes;
1259 p_in_buffer = psz_string;
1260 p_out_buffer = (char *)psz_unicode;
1261 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1262 &p_out_buffer, &i_out_bytes_left );
1264 vlc_iconv_close( iconv_handle );
1268 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1269 "bytes left %d", strerror(errno), (int)i_in_bytes );
1270 free( psz_unicode );
1273 *(uint32_t*)p_out_buffer = 0;
1274 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1277 #if defined(HAVE_FRIBIDI)
1279 uint32_t *p_fribidi_string;
1281 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1283 /* Do bidi conversion line-by-line */
1284 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1285 fribidi_log2vis((FriBidiChar*)psz_unicode, i_string_length,
1286 &base_dir, (FriBidiChar*)p_fribidi_string, 0, 0, 0);
1288 free( psz_unicode );
1289 psz_unicode = p_fribidi_string;
1290 p_fribidi_string[ i_string_length ] = 0;
1296 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1297 vlc_bool_t b_uline, line_desc_t *p_line, uint32_t *psz_unicode,
1298 int *pi_pen_x, int i_pen_y, int *pi_start,
1299 FT_Vector *p_result )
1306 int i_pen_x_start = *pi_pen_x;
1308 uint32_t *psz_unicode_start = psz_unicode;
1310 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1312 /* Account for part of line already in position */
1313 for( i=0; i<*pi_start; i++ )
1317 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ], ft_glyph_bbox_pixels, &glyph_size );
1319 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1320 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1321 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1322 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1327 while( *psz_unicode && ( *psz_unicode != 0xffff ) )
1333 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1334 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1338 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1339 ft_kerning_default, &delta );
1340 *pi_pen_x += delta.x >> 6;
1342 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1343 p_line->p_glyph_pos[ i ].y = i_pen_y;
1345 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1348 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned %d", i_error );
1349 p_line->pp_glyphs[ i ] = NULL;
1350 return VLC_EGENERIC;
1352 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1355 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned %d", i_error );
1356 p_line->pp_glyphs[ i ] = NULL;
1357 return VLC_EGENERIC;
1359 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1360 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1363 FT_Done_Glyph( tmp_glyph );
1368 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position, p_face->size->metrics.y_scale));
1369 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness, p_face->size->metrics.y_scale));
1371 p_line->pi_underline_offset[ i ] = ( aOffset < 0 ) ? -aOffset : aOffset;
1372 p_line->pi_underline_thickness[ i ] = ( aSize < 0 ) ? -aSize : aSize;
1374 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1375 p_line->p_rgb[ i ] = i_font_color & 0x00ffffff;
1377 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1378 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1379 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1381 while( --i > *pi_start )
1383 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1386 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1390 if( psz_unicode == psz_unicode_start )
1392 msg_Warn( p_filter, "unbreakable string" );
1393 p_line->pp_glyphs[ i ] = NULL;
1394 return VLC_EGENERIC;
1398 *psz_unicode = 0xffff;
1400 psz_unicode = psz_unicode_start;
1401 *pi_pen_x = i_pen_x_start;
1409 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1410 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1412 i_previous = i_glyph_index;
1413 *pi_pen_x += p_face->glyph->advance.x >> 6;
1416 p_line->i_width = line.xMax;
1417 p_line->i_height = __MAX( p_line->i_height, p_face->size->metrics.height >> 6 );
1418 p_line->pp_glyphs[ i ] = NULL;
1420 p_result->x = __MAX( p_result->x, line.xMax );
1421 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height, line.yMax - line.yMin ) );
1425 /* Get rid of any text processed - if necessary repositioning
1426 * at the start of a new line of text
1430 *psz_unicode_start = '\0';
1435 for( i=0; psz_unicode[ i ]; i++ )
1436 psz_unicode_start[ i ] = psz_unicode[ i ];
1437 psz_unicode_start[ i ] = '\0';
1443 static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader, char *psz_html, text_style_t *p_font_style, line_desc_t **p_lines, FT_Vector *p_result )
1445 filter_sys_t *p_sys = p_filter->p_sys;
1447 FT_Vector tmp_result;
1449 font_stack_t *p_fonts = NULL;
1450 vlc_bool_t b_italic = VLC_FALSE;
1451 vlc_bool_t b_bold = VLC_FALSE;
1452 vlc_bool_t b_uline = VLC_FALSE;
1454 line_desc_t *p_line = NULL;
1455 line_desc_t *p_prev = NULL;
1457 char *psz_node = NULL;
1463 int rv = VLC_SUCCESS;
1465 p_result->x = p_result->y = 0;
1466 tmp_result.x = tmp_result.y = 0;
1471 p_font_style->psz_fontname,
1472 p_font_style->i_font_size,
1473 p_font_style->i_font_color,
1474 p_font_style->i_font_alpha );
1476 if( p_font_style->i_style_flags & STYLE_BOLD )
1478 if( p_font_style->i_style_flags & STYLE_ITALIC )
1479 b_italic = VLC_TRUE;
1480 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1485 PushFont( &p_fonts, FC_DEFAULT_FONT, p_sys->i_font_size, 0xffffff, 0 );
1488 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) && ( rv == VLC_SUCCESS ) )
1490 switch ( xml_ReaderNodeType( p_xml_reader ) )
1492 case XML_READER_NONE:
1494 case XML_READER_ENDELEM:
1495 psz_node = xml_ReaderName( p_xml_reader );
1499 if( !strcasecmp( "font", psz_node ) )
1500 PopFont( &p_fonts );
1501 else if( !strcasecmp( "b", psz_node ) )
1503 else if( !strcasecmp( "i", psz_node ) )
1504 b_italic = VLC_FALSE;
1505 else if( !strcasecmp( "u", psz_node ) )
1506 b_uline = VLC_FALSE;
1511 case XML_READER_STARTELEM:
1512 psz_node = xml_ReaderName( p_xml_reader );
1515 if( !strcasecmp( "font", psz_node ) )
1517 char *psz_fontname = NULL;
1518 int i_font_color = 0xffffff;
1519 int i_font_alpha = 0;
1520 int i_font_size = 24;
1522 /* Default all attributes to the top font in the stack -- in case not
1523 * all attributes are specified in the sub-font
1525 if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ))
1527 psz_fontname = strdup( psz_fontname );
1530 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1532 char *psz_name = xml_ReaderName ( p_xml_reader );
1533 char *psz_value = xml_ReaderValue ( p_xml_reader );
1535 if( psz_name && psz_value )
1537 if( !strcasecmp( "face", psz_name ) )
1539 if( psz_fontname ) free( psz_fontname );
1540 psz_fontname = strdup( psz_value );
1542 else if( !strcasecmp( "size", psz_name ) )
1544 i_font_size = atoi( psz_value );
1546 else if( !strcasecmp( "color", psz_name ) &&
1547 ( psz_value[0] == '#' ) )
1549 i_font_color = strtol( psz_value+1, NULL, 16 );
1550 i_font_color &= 0x00ffffff;
1552 else if( !strcasecmp( "alpha", psz_name ) &&
1553 ( psz_value[0] == '#' ) )
1555 i_font_alpha = strtol( psz_value+1, NULL, 16 );
1556 i_font_alpha &= 0xff;
1562 PushFont( &p_fonts, psz_fontname, i_font_size, i_font_color, i_font_alpha );
1563 free( psz_fontname );
1565 else if( !strcasecmp( "b", psz_node ) )
1569 else if( !strcasecmp( "i", psz_node ) )
1571 b_italic = VLC_TRUE;
1573 else if( !strcasecmp( "u", psz_node ) )
1577 else if( !strcasecmp( "br", psz_node ) )
1582 if( !(p_line = NewLine( (byte_t *)psz_html )) )
1584 msg_Err( p_filter, "out of memory" );
1589 p_line->b_new_color_mode = VLC_TRUE;
1590 p_result->x = __MAX( p_result->x, tmp_result.x );
1591 p_result->y += tmp_result.y;
1593 p_line->p_next = NULL;
1595 i_pen_y += tmp_result.y;
1597 p_prev->p_next = p_line;
1605 case XML_READER_TEXT:
1606 psz_node = xml_ReaderValue( p_xml_reader );
1609 char *psz_fontname = NULL;
1610 int i_font_color = 0xffffff;
1611 int i_font_alpha = 0;
1612 int i_font_size = 24;
1613 FT_Face p_face = NULL;
1615 if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ) )
1618 char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig, psz_fontname, b_bold, b_italic, &i_idx );
1622 if( FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
1625 free( psz_fontfile );
1630 free( psz_fontfile );
1634 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face, ft_encoding_unicode ) ||
1635 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0, i_font_size ) )
1641 p_sys->i_use_kerning = FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1643 uint32_t *psz_unicode = IconvText( p_filter, psz_node );
1648 if( p_face ) FT_Done_Face( p_face );
1653 while( *psz_unicode )
1657 if( !(p_line = NewLine( (byte_t *)psz_html )) )
1659 msg_Err( p_filter, "out of memory" );
1661 if( p_face ) FT_Done_Face( p_face );
1665 /* New Color mode only works in YUVA rendering mode --
1666 * (RGB mode has palette constraints on it). We therefore
1667 * need to populate the legacy colour fields also.
1669 p_line->b_new_color_mode = VLC_TRUE;
1670 p_line->i_alpha = i_font_alpha;
1671 p_line->i_red = ( i_font_color & 0xff0000 ) >> 16;
1672 p_line->i_green = ( i_font_color & 0x00ff00 ) >> 8;
1673 p_line->i_blue = ( i_font_color & 0x0000ff );
1674 p_line->p_next = NULL;
1676 i_pen_y += tmp_result.y;
1680 if( p_prev ) p_prev->p_next = p_line;
1681 else *p_lines = p_line;
1684 if( RenderTag( p_filter, p_face, i_font_color, b_uline, p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn, &tmp_result ) != VLC_SUCCESS )
1687 if( p_face ) FT_Done_Face( p_face );
1693 p_result->x = __MAX( p_result->x, tmp_result.x );
1694 p_result->y += tmp_result.y;
1700 if( rv != VLC_SUCCESS ) break;
1702 if( p_face ) FT_Done_Face( p_face );
1703 free( psz_unicode );
1711 p_result->x = __MAX( p_result->x, tmp_result.x );
1712 p_result->y += tmp_result.y;
1716 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1722 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
1723 subpicture_region_t *p_region_in )
1725 int rv = VLC_SUCCESS;
1726 stream_t *p_sub = NULL;
1727 xml_t *p_xml = NULL;
1728 xml_reader_t *p_xml_reader = NULL;
1730 if( !p_region_in || !p_region_in->psz_html )
1731 return VLC_EGENERIC;
1733 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
1734 (uint8_t *) p_region_in->psz_html,
1735 strlen( p_region_in->psz_html ),
1739 p_xml = xml_Create( p_filter );
1742 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
1746 line_desc_t *p_lines = NULL;
1748 rv = ProcessNodes( p_filter, p_xml_reader, p_region_in->psz_html, p_region_in->p_style, &p_lines, &result );
1750 if( rv == VLC_SUCCESS )
1752 p_region_out->i_x = p_region_in->i_x;
1753 p_region_out->i_y = p_region_in->i_y;
1755 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1756 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1758 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1760 FreeLines( p_lines );
1762 xml_ReaderDelete( p_xml, p_xml_reader );
1764 xml_Delete( p_xml );
1766 stream_Delete( p_sub );
1772 static char* FontConfig_Select( FcConfig* priv, const char* family, vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
1775 FcPattern *pat, *p_pat;
1779 pat = FcPatternCreate();
1780 if (!pat) return NULL;
1782 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
1783 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
1784 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
1785 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
1787 FcDefaultSubstitute( pat );
1789 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
1791 FcPatternDestroy( pat );
1795 p_pat = FcFontMatch( priv, pat, &result );
1796 FcPatternDestroy( pat );
1797 if( !p_pat ) return NULL;
1799 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) ) ||
1800 ( val_b != FcTrue ) )
1802 FcPatternDestroy( p_pat );
1805 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
1810 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
1812 FcPatternDestroy( p_pat );
1817 if( strcasecmp((const char*)val_s, family ) != 0 )
1818 msg_Warn( p_filter, "fontconfig: selected font family is not the requested one: '%s' != '%s'\n",
1819 (const char*)val_s, family );
1822 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
1824 FcPatternDestroy( p_pat );
1828 FcPatternDestroy( p_pat );
1829 return strdup( (const char*)val_s );
1833 static void FreeLine( line_desc_t *p_line )
1836 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
1838 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1840 free( p_line->pp_glyphs );
1841 free( p_line->p_glyph_pos );
1842 free( p_line->p_rgb );
1843 free( p_line->pi_underline_offset );
1844 free( p_line->pi_underline_thickness );
1848 static void FreeLines( line_desc_t *p_lines )
1850 line_desc_t *p_line, *p_next;
1852 if( !p_lines ) return;
1854 for( p_line = p_lines; p_line != NULL; p_line = p_next )
1856 p_next = p_line->p_next;
1861 static line_desc_t *NewLine( byte_t *psz_string )
1864 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1866 if( !p_line ) return NULL;
1867 p_line->i_height = 0;
1868 p_line->i_width = 0;
1869 p_line->p_next = NULL;
1871 /* We don't use CountUtf8Characters() here because we are not acutally
1872 * sure the string is utf8. Better be safe than sorry. */
1873 i_count = strlen( (char *)psz_string );
1875 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1876 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1877 p_line->p_rgb = malloc( sizeof( uint32_t ) * i_count + 1 );
1878 p_line->pi_underline_offset = calloc( i_count+ + 1, sizeof( uint16_t ) );
1879 p_line->pi_underline_thickness = calloc( i_count+ + 1, sizeof( uint16_t ) );
1880 if( ( p_line->pp_glyphs == NULL ) ||
1881 ( p_line->p_glyph_pos == NULL ) ||
1882 ( p_line->p_rgb == NULL ) ||
1883 ( p_line->pi_underline_offset == NULL ) ||
1884 ( p_line->pi_underline_thickness == NULL ) )
1886 if( p_line->pi_underline_thickness ) free( p_line->pi_underline_thickness );
1887 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
1888 if( p_line->p_rgb ) free( p_line->p_rgb );
1889 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
1890 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
1894 p_line->pp_glyphs[0] = NULL;
1895 p_line->b_new_color_mode = VLC_FALSE;
1900 static int SetFontSize( filter_t *p_filter, int i_size )
1902 filter_sys_t *p_sys = p_filter->p_sys;
1904 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1910 if( !p_sys->i_default_font_size &&
1911 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1914 if( p_sys->i_default_font_size )
1916 i_size = p_sys->i_default_font_size;
1920 var_Get( p_filter, "freetype-rel-fontsize", &val );
1921 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1922 p_filter->p_sys->i_display_height =
1923 p_filter->fmt_out.video.i_height;
1927 msg_Warn( p_filter, "invalid fontsize, using 12" );
1931 msg_Dbg( p_filter, "using fontsize: %i", i_size );
1934 p_sys->i_font_size = i_size;
1936 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1938 msg_Err( p_filter, "couldn't set font size to %d", i_size );
1939 return VLC_EGENERIC;