Old RC: "rate" is a float nowadays (fix #4088)
[vlc/asuraparaju-public.git] / modules / misc / freetype.c
blob22cb97027c586836f871ee002eec4455898426a7
1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
5 * $Id$
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 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include <vlc_stream.h>
38 #include <vlc_xml.h>
39 #include <vlc_input.h>
40 #include <vlc_strings.h>
41 #include <vlc_dialog.h>
42 #include <vlc_memory.h>
44 #include <math.h>
46 #include <ft2build.h>
47 #include <freetype/ftsynth.h>
48 #include FT_FREETYPE_H
49 #include FT_GLYPH_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
52 #ifndef FT_MulFix
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
54 #endif
56 #ifdef __APPLE__
57 #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
58 #define FC_DEFAULT_FONT "Arial Black"
59 #elif defined( SYS_BEOS )
60 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
61 #define FC_DEFAULT_FONT "Swiss"
62 #elif defined( WIN32 )
63 #define DEFAULT_FONT "" /* Default font found at run-time */
64 #define FC_DEFAULT_FONT "Arial"
65 #elif defined( HAVE_MAEMO )
66 #define DEFAULT_FONT "/usr/share/fonts/nokia/nosnb.ttf"
67 #define FC_DEFAULT_FONT "Nokia Sans Bold"
68 #else
69 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
70 #define FC_DEFAULT_FONT "Serif Bold"
71 #endif
73 #if defined(HAVE_FRIBIDI)
74 #include <fribidi/fribidi.h>
75 #endif
77 #ifdef HAVE_FONTCONFIG
78 #include <fontconfig/fontconfig.h>
79 #undef DEFAULT_FONT
80 #define DEFAULT_FONT FC_DEFAULT_FONT
81 #endif
83 #include <assert.h>
85 /*****************************************************************************
86 * Module descriptor
87 *****************************************************************************/
88 static int Create ( vlc_object_t * );
89 static void Destroy( vlc_object_t * );
91 #define FONT_TEXT N_("Font")
93 #ifdef HAVE_FONTCONFIG
94 #define FONT_LONGTEXT N_("Font family for the font you want to use")
95 #else
96 #define FONT_LONGTEXT N_("Font file for the font you want to use")
97 #endif
99 #define FONTSIZE_TEXT N_("Font size in pixels")
100 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
101 "that will be rendered on the video. " \
102 "If set to something different than 0 this option will override the " \
103 "relative font size." )
104 #define OPACITY_TEXT N_("Opacity")
105 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
106 "text that will be rendered on the video. 0 = transparent, " \
107 "255 = totally opaque. " )
108 #define COLOR_TEXT N_("Text default color")
109 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
110 "the video. This must be an hexadecimal (like HTML colors). The first two "\
111 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
112 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
113 #define FONTSIZER_TEXT N_("Relative font size")
114 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
115 "fonts that will be rendered on the video. If absolute font size is set, "\
116 "relative size will be overridden." )
118 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
119 static const char *const ppsz_sizes_text[] = {
120 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
121 #define YUVP_TEXT N_("Use YUVP renderer")
122 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
123 "This option is only needed if you want to encode into DVB subtitles" )
124 #define EFFECT_TEXT N_("Font Effect")
125 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
126 "text to improve its readability." )
128 #define EFFECT_BACKGROUND 1
129 #define EFFECT_OUTLINE 2
130 #define EFFECT_OUTLINE_FAT 3
132 static int const pi_effects[] = { 1, 2, 3 };
133 static const char *const ppsz_effects_text[] = {
134 N_("Background"),N_("Outline"), N_("Fat Outline") };
135 static const int pi_color_values[] = {
136 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
137 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
138 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
140 static const char *const ppsz_color_descriptions[] = {
141 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
142 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
143 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
145 vlc_module_begin ()
146 set_shortname( N_("Text renderer"))
147 set_description( N_("Freetype2 font renderer") )
148 set_category( CAT_VIDEO )
149 set_subcategory( SUBCAT_VIDEO_SUBPIC )
151 add_font( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
152 false )
154 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
155 FONTSIZE_LONGTEXT, true )
157 /* opacity valid on 0..255, with default 255 = fully opaque */
158 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
159 OPACITY_TEXT, OPACITY_LONGTEXT, true )
161 /* hook to the color values list, with default 0x00ffffff = white */
162 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
163 COLOR_LONGTEXT, false )
164 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
166 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
167 FONTSIZER_LONGTEXT, false )
168 change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
169 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
170 EFFECT_LONGTEXT, false )
171 change_integer_list( pi_effects, ppsz_effects_text, NULL )
173 add_bool( "freetype-yuvp", false, NULL, YUVP_TEXT,
174 YUVP_LONGTEXT, true )
175 set_capability( "text renderer", 100 )
176 add_shortcut( "text" )
177 set_callbacks( Create, Destroy )
178 vlc_module_end ()
182 /*****************************************************************************
183 * Local prototypes
184 *****************************************************************************/
186 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
187 static int RenderText( filter_t *, subpicture_region_t *,
188 subpicture_region_t * );
189 #ifdef HAVE_FONTCONFIG
190 static int RenderHtml( filter_t *, subpicture_region_t *,
191 subpicture_region_t * );
192 static char *FontConfig_Select( FcConfig *, const char *,
193 bool, bool, int * );
194 #endif
197 static int LoadFontsFromAttachments( filter_t *p_filter );
199 static int GetFontSize( filter_t *p_filter );
200 static int SetFontSize( filter_t *, int );
201 static void YUVFromRGB( uint32_t i_argb,
202 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
204 typedef struct line_desc_t line_desc_t;
205 struct line_desc_t
207 /** NULL-terminated list of glyphs making the string */
208 FT_BitmapGlyph *pp_glyphs;
209 /** list of relative positions for the glyphs */
210 FT_Vector *p_glyph_pos;
211 /** list of RGB information for styled text
212 * -- if the rendering mode supports it (RenderYUVA) and
213 * b_new_color_mode is set, then it becomes possible to
214 * have multicoloured text within the subtitles. */
215 uint32_t *p_fg_rgb;
216 uint32_t *p_bg_rgb;
217 uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
218 bool b_new_color_mode;
219 /** underline information -- only supplied if text should be underlined */
220 int *pi_underline_offset;
221 uint16_t *pi_underline_thickness;
223 int i_height;
224 int i_width;
225 int i_red, i_green, i_blue;
226 int i_alpha;
228 line_desc_t *p_next;
230 static line_desc_t *NewLine( int );
232 typedef struct
234 int i_font_size;
235 uint32_t i_font_color; /* ARGB */
236 uint32_t i_karaoke_bg_color; /* ARGB */
237 bool b_italic;
238 bool b_bold;
239 bool b_underline;
240 bool b_through;
241 char *psz_fontname;
242 } ft_style_t;
244 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
245 static void FreeLines( line_desc_t * );
246 static void FreeLine( line_desc_t * );
248 /*****************************************************************************
249 * filter_sys_t: freetype local data
250 *****************************************************************************
251 * This structure is part of the video output thread descriptor.
252 * It describes the freetype specific properties of an output thread.
253 *****************************************************************************/
254 struct filter_sys_t
256 FT_Library p_library; /* handle to library */
257 FT_Face p_face; /* handle to face object */
258 bool i_use_kerning;
259 uint8_t i_font_opacity;
260 int i_font_color;
261 int i_font_size;
262 int i_effect;
264 int i_default_font_size;
265 int i_display_height;
266 #ifdef HAVE_FONTCONFIG
267 char* psz_fontfamily;
268 xml_reader_t *p_xml;
269 #endif
271 input_attachment_t **pp_font_attachments;
272 int i_font_attachments;
276 #define UCHAR uint32_t
277 #define TR_DEFAULT_FONT p_sys->psz_fontfamily
278 #define TR_FONT_STYLE_PTR ft_style_t *
280 #include "text_renderer.h"
282 /*****************************************************************************
283 * Create: allocates osd-text video thread output method
284 *****************************************************************************
285 * This function allocates and initializes a Clone vout method.
286 *****************************************************************************/
287 static int Create( vlc_object_t *p_this )
289 filter_t *p_filter = (filter_t *)p_this;
290 filter_sys_t *p_sys;
291 char *psz_fontfile=NULL;
292 char *psz_fontfamily=NULL;
293 int i_error,fontindex;
295 #ifdef HAVE_FONTCONFIG
296 FcPattern *fontpattern = NULL, *fontmatch = NULL;
297 /* Initialise result to Match, as fontconfig doesnt
298 * really set this other than some error-cases */
299 FcResult fontresult = FcResultMatch;
300 #endif
303 /* Allocate structure */
304 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
305 if( !p_sys )
306 return VLC_ENOMEM;
307 #ifdef HAVE_FONTCONFIG
308 p_sys->psz_fontfamily = NULL;
309 p_sys->p_xml = NULL;
310 #endif
311 p_sys->p_face = 0;
312 p_sys->p_library = 0;
313 p_sys->i_font_size = 0;
314 p_sys->i_display_height = 0;
316 var_Create( p_filter, "freetype-rel-fontsize",
317 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
319 psz_fontfamily = var_CreateGetString( p_filter, "freetype-font" );
320 p_sys->i_default_font_size = var_CreateGetInteger( p_filter, "freetype-fontsize" );
321 p_sys->i_effect = var_CreateGetInteger( p_filter, "freetype-effect" );
322 p_sys->i_font_opacity = var_CreateGetInteger( p_filter,"freetype-opacity" );
323 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
324 p_sys->i_font_color = var_CreateGetInteger( p_filter, "freetype-color" );
325 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
327 fontindex=0;
328 if( !psz_fontfamily || !*psz_fontfamily )
330 free( psz_fontfamily );
331 #ifdef HAVE_FONTCONFIG
332 psz_fontfamily=strdup( DEFAULT_FONT );
333 #else
334 psz_fontfamily = (char *)malloc( PATH_MAX + 1 );
335 if( !psz_fontfamily )
336 goto error;
337 # ifdef WIN32
338 GetWindowsDirectory( psz_fontfamily , PATH_MAX + 1 );
339 strcat( psz_fontfamily, "\\fonts\\arial.ttf" );
340 # else
341 strcpy( psz_fontfamily, DEFAULT_FONT );
342 # endif
343 msg_Err( p_filter,"User didn't specify fontfile, using %s", psz_fontfamily);
344 #endif
347 #ifdef HAVE_FONTCONFIG
348 msg_Dbg( p_filter, "Building font databases.");
349 mtime_t t1, t2;
350 t1 = mdate();
352 #ifdef WIN32
353 dialog_progress_bar_t *p_dialog = NULL;
354 FcConfig *fcConfig = FcInitLoadConfig();
356 p_dialog = dialog_ProgressCreate( p_filter,
357 _("Building font cache"),
358 _("Please wait while your font cache is rebuilt.\n"
359 "This should take less than a few minutes."), NULL );
361 /* if( p_dialog )
362 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
364 FcConfigBuildFonts( fcConfig );
365 t2 = mdate();
366 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
368 if( p_dialog )
370 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
371 dialog_ProgressDestroy( p_dialog );
372 p_dialog = NULL;
374 #endif
375 /* Lets find some fontfile from freetype-font variable family */
376 char *psz_fontsize;
377 if( asprintf( &psz_fontsize, "%d", p_sys->i_default_font_size ) == -1 )
378 goto error;
380 fontpattern = FcPatternCreate();
381 if( !fontpattern )
383 msg_Err( p_filter, "Creating fontpattern failed");
384 goto error;
387 FcPatternAddString( fontpattern, FC_FAMILY, psz_fontfamily);
388 FcPatternAddString( fontpattern, FC_SIZE, psz_fontsize );
389 free( psz_fontsize );
391 if( FcConfigSubstitute( NULL, fontpattern, FcMatchPattern ) == FcFalse )
393 msg_Err( p_filter, "FontSubstitute failed");
394 goto error;
396 FcDefaultSubstitute( fontpattern );
398 /* testing fontresult here doesn't do any good really, but maybe it will
399 * in future as fontconfig code doesn't set it in all cases and just
400 * returns NULL or doesn't set to to Match on all Match cases.*/
401 fontmatch = FcFontMatch( NULL, fontpattern, &fontresult );
402 if( !fontmatch || fontresult == FcResultNoMatch )
404 msg_Err( p_filter, "Fontmatching failed");
405 goto error;
408 FcPatternGetString( fontmatch, FC_FILE, 0, &psz_fontfile);
409 FcPatternGetInteger( fontmatch, FC_INDEX, 0, &fontindex );
410 if( !psz_fontfile )
412 msg_Err( p_filter, "Failed to get fontfile");
413 goto error;
416 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily,
417 psz_fontfile ? psz_fontfile : "(null)" );
418 p_sys->psz_fontfamily = strdup( psz_fontfamily );
420 #else
422 psz_fontfile = psz_fontfamily;
424 #endif
426 i_error = FT_Init_FreeType( &p_sys->p_library );
427 if( i_error )
429 msg_Err( p_filter, "couldn't initialize freetype" );
430 goto error;
433 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
434 fontindex, &p_sys->p_face );
436 if( i_error == FT_Err_Unknown_File_Format )
438 msg_Err( p_filter, "file %s have unknown format",
439 psz_fontfile ? psz_fontfile : "(null)" );
440 goto error;
442 else if( i_error )
444 msg_Err( p_filter, "failed to load font file %s",
445 psz_fontfile ? psz_fontfile : "(null)" );
446 goto error;
449 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
450 if( i_error )
452 msg_Err( p_filter, "font has no unicode translation table" );
453 goto error;
456 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
458 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
461 p_sys->pp_font_attachments = NULL;
462 p_sys->i_font_attachments = 0;
464 p_filter->pf_render_text = RenderText;
465 #ifdef HAVE_FONTCONFIG
466 p_filter->pf_render_html = RenderHtml;
467 FcPatternDestroy( fontmatch );
468 FcPatternDestroy( fontpattern );
469 #else
470 p_filter->pf_render_html = NULL;
471 #endif
473 free( psz_fontfamily );
474 LoadFontsFromAttachments( p_filter );
476 return VLC_SUCCESS;
478 error:
479 #ifdef HAVE_FONTCONFIG
480 if( fontmatch ) FcPatternDestroy( fontmatch );
481 if( fontpattern ) FcPatternDestroy( fontpattern );
482 #endif
484 #ifdef WIN32
485 if( p_dialog )
486 dialog_ProgressDestroy( p_dialog );
487 #endif
489 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
490 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
491 free( psz_fontfamily );
492 free( p_sys );
493 return VLC_EGENERIC;
496 /*****************************************************************************
497 * Destroy: destroy Clone video thread output method
498 *****************************************************************************
499 * Clean up all data and library connections
500 *****************************************************************************/
501 static void Destroy( vlc_object_t *p_this )
503 filter_t *p_filter = (filter_t *)p_this;
504 filter_sys_t *p_sys = p_filter->p_sys;
506 if( p_sys->pp_font_attachments )
508 int k;
510 for( k = 0; k < p_sys->i_font_attachments; k++ )
511 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
513 free( p_sys->pp_font_attachments );
516 #ifdef HAVE_FONTCONFIG
517 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
518 free( p_sys->psz_fontfamily );
519 #endif
521 /* FcFini asserts calling the subfunction FcCacheFini()
522 * even if no other library functions have been made since FcInit(),
523 * so don't call it. */
525 FT_Done_Face( p_sys->p_face );
526 FT_Done_FreeType( p_sys->p_library );
527 free( p_sys );
530 /*****************************************************************************
531 * Make any TTF/OTF fonts present in the attachments of the media file
532 * and store them for later use by the FreeType Engine
533 *****************************************************************************/
534 static int LoadFontsFromAttachments( filter_t *p_filter )
536 filter_sys_t *p_sys = p_filter->p_sys;
537 input_attachment_t **pp_attachments;
538 int i_attachments_cnt;
540 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
541 return VLC_EGENERIC;
543 p_sys->i_font_attachments = 0;
544 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
545 if( !p_sys->pp_font_attachments )
546 return VLC_ENOMEM;
548 for( int k = 0; k < i_attachments_cnt; k++ )
550 input_attachment_t *p_attach = pp_attachments[k];
552 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
553 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
554 p_attach->i_data > 0 && p_attach->p_data )
556 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
558 else
560 vlc_input_attachment_Delete( p_attach );
563 free( pp_attachments );
565 return VLC_SUCCESS;
568 /*****************************************************************************
569 * Render: place string in picture
570 *****************************************************************************
571 * This function merges the previously rendered freetype glyphs into a picture
572 *****************************************************************************/
573 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
574 line_desc_t *p_line, int i_width, int i_height )
576 VLC_UNUSED(p_filter);
577 static const uint8_t pi_gamma[16] =
578 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
579 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
581 uint8_t *p_dst;
582 video_format_t fmt;
583 int i, x, y, i_pitch;
584 uint8_t i_y; /* YUV values, derived from incoming RGB */
585 int8_t i_u, i_v;
587 /* Create a new subpicture region */
588 memset( &fmt, 0, sizeof(video_format_t) );
589 fmt.i_chroma = VLC_CODEC_YUVP;
590 fmt.i_width = fmt.i_visible_width = i_width + 4;
591 fmt.i_height = fmt.i_visible_height = i_height + 4;
592 if( p_region->fmt.i_visible_width > 0 )
593 fmt.i_visible_width = p_region->fmt.i_visible_width;
594 if( p_region->fmt.i_visible_height > 0 )
595 fmt.i_visible_height = p_region->fmt.i_visible_height;
596 fmt.i_x_offset = fmt.i_y_offset = 0;
598 assert( !p_region->p_picture );
599 p_region->p_picture = picture_NewFromFormat( &fmt );
600 if( !p_region->p_picture )
601 return VLC_EGENERIC;
602 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
603 p_region->fmt = fmt;
605 /* Calculate text color components */
606 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
607 25 * p_line->i_blue + 128) >> 8) + 16;
608 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
609 112 * p_line->i_blue + 128) >> 8) + 128;
610 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
611 18 * p_line->i_blue + 128) >> 8) + 128;
613 /* Build palette */
614 fmt.p_palette->i_entries = 16;
615 for( i = 0; i < 8; i++ )
617 fmt.p_palette->palette[i][0] = 0;
618 fmt.p_palette->palette[i][1] = 0x80;
619 fmt.p_palette->palette[i][2] = 0x80;
620 fmt.p_palette->palette[i][3] = pi_gamma[i];
621 fmt.p_palette->palette[i][3] =
622 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
624 for( i = 8; i < fmt.p_palette->i_entries; i++ )
626 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
627 fmt.p_palette->palette[i][1] = i_u;
628 fmt.p_palette->palette[i][2] = i_v;
629 fmt.p_palette->palette[i][3] = pi_gamma[i];
630 fmt.p_palette->palette[i][3] =
631 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
634 p_dst = p_region->p_picture->Y_PIXELS;
635 i_pitch = p_region->p_picture->Y_PITCH;
637 /* Initialize the region pixels */
638 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
640 for( ; p_line != NULL; p_line = p_line->p_next )
642 int i_glyph_tmax = 0;
643 int i_bitmap_offset, i_offset, i_align_offset = 0;
644 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
646 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
647 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
650 if( p_line->i_width < i_width )
652 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
654 i_align_offset = i_width - p_line->i_width;
656 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
658 i_align_offset = ( i_width - p_line->i_width ) / 2;
662 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
664 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
666 i_offset = ( p_line->p_glyph_pos[ i ].y +
667 i_glyph_tmax - p_glyph->top + 2 ) *
668 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
669 i_align_offset;
671 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
673 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
675 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
676 p_dst[i_offset+x] =
677 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
679 i_offset += i_pitch;
684 /* Outlining (find something better than nearest neighbour filtering ?) */
685 if( 1 )
687 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
688 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
689 uint8_t left, current;
691 for( y = 1; y < (int)fmt.i_height - 1; y++ )
693 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
694 p_dst += p_region->p_picture->Y_PITCH;
695 left = 0;
697 for( x = 1; x < (int)fmt.i_width - 1; x++ )
699 current = p_dst[x];
700 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
701 p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
702 left = current;
705 memset( p_top, 0, fmt.i_width );
708 return VLC_SUCCESS;
711 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
712 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
713 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
714 int i_glyph_tmax, int i_align_offset,
715 uint8_t i_y, uint8_t i_u, uint8_t i_v,
716 subpicture_region_t *p_region)
718 int y, x, z;
719 int i_pitch;
720 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
722 p_dst_y = p_region->p_picture->Y_PIXELS;
723 p_dst_u = p_region->p_picture->U_PIXELS;
724 p_dst_v = p_region->p_picture->V_PIXELS;
725 p_dst_a = p_region->p_picture->A_PIXELS;
726 i_pitch = p_region->p_picture->A_PITCH;
728 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
729 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
731 for( y = 0; y < i_line_thickness; y++ )
733 int i_extra = p_this_glyph->bitmap.width;
735 if( b_ul_next_char )
737 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
738 (p_this_glyph_pos->x + p_this_glyph->left);
740 for( x = 0; x < i_extra; x++ )
742 bool b_ok = true;
744 /* break the underline around the tails of any glyphs which cross it */
745 /* Strikethrough doesn't get broken */
746 for( z = x - i_line_thickness;
747 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
748 z++ )
750 if( p_next_glyph && ( z >= i_extra ) )
752 int i_row = i_line_offset + p_next_glyph->top + y;
754 if( ( p_next_glyph->bitmap.rows > i_row ) &&
755 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
757 b_ok = false;
760 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
762 int i_row = i_line_offset + p_this_glyph->top + y;
764 if( ( p_this_glyph->bitmap.rows > i_row ) &&
765 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
767 b_ok = false;
772 if( b_ok )
774 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
775 p_dst_u[i_offset+x] = i_u;
776 p_dst_v[i_offset+x] = i_v;
777 p_dst_a[i_offset+x] = 255;
780 i_offset += i_pitch;
784 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
786 uint8_t *p_dst = p_region->p_picture->A_PIXELS;
787 int i_pitch = p_region->p_picture->A_PITCH;
788 int x,y;
790 for( ; p_line != NULL; p_line = p_line->p_next )
792 int i_glyph_tmax=0, i = 0;
793 int i_bitmap_offset, i_offset, i_align_offset = 0;
794 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
796 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
797 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
800 if( p_line->i_width < i_width )
802 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
804 i_align_offset = i_width - p_line->i_width;
806 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
808 i_align_offset = ( i_width - p_line->i_width ) / 2;
812 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
814 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
816 i_offset = ( p_line->p_glyph_pos[ i ].y +
817 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
818 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
819 i_align_offset +xoffset;
821 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
823 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
825 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
826 if( p_dst[i_offset+x] <
827 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
828 p_dst[i_offset+x] =
829 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
831 i_offset += i_pitch;
838 /*****************************************************************************
839 * Render: place string in picture
840 *****************************************************************************
841 * This function merges the previously rendered freetype glyphs into a picture
842 *****************************************************************************/
843 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
844 line_desc_t *p_line, int i_width, int i_height )
846 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
847 video_format_t fmt;
848 int i, x, y, i_pitch, i_alpha;
849 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
851 if( i_width == 0 || i_height == 0 )
852 return VLC_SUCCESS;
854 /* Create a new subpicture region */
855 memset( &fmt, 0, sizeof(video_format_t) );
856 fmt.i_chroma = VLC_CODEC_YUVA;
857 fmt.i_width = fmt.i_visible_width = i_width + 6;
858 fmt.i_height = fmt.i_visible_height = i_height + 6;
859 if( p_region->fmt.i_visible_width > 0 )
860 fmt.i_visible_width = p_region->fmt.i_visible_width;
861 if( p_region->fmt.i_visible_height > 0 )
862 fmt.i_visible_height = p_region->fmt.i_visible_height;
863 fmt.i_x_offset = fmt.i_y_offset = 0;
865 p_region->p_picture = picture_NewFromFormat( &fmt );
866 if( !p_region->p_picture )
867 return VLC_EGENERIC;
868 p_region->fmt = fmt;
870 /* Calculate text color components */
871 YUVFromRGB( (p_line->i_red << 16) |
872 (p_line->i_green << 8) |
873 (p_line->i_blue ),
874 &i_y, &i_u, &i_v);
875 i_alpha = p_line->i_alpha;
877 p_dst_y = p_region->p_picture->Y_PIXELS;
878 p_dst_u = p_region->p_picture->U_PIXELS;
879 p_dst_v = p_region->p_picture->V_PIXELS;
880 p_dst_a = p_region->p_picture->A_PIXELS;
881 i_pitch = p_region->p_picture->A_PITCH;
883 /* Initialize the region pixels */
884 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
886 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
887 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
888 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
889 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
891 else
893 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
894 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
895 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
896 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
898 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
899 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
901 DrawBlack( p_line, i_width, p_region, 0, 0);
902 DrawBlack( p_line, i_width, p_region, -1, 0);
903 DrawBlack( p_line, i_width, p_region, 0, -1);
904 DrawBlack( p_line, i_width, p_region, 1, 0);
905 DrawBlack( p_line, i_width, p_region, 0, 1);
908 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
910 DrawBlack( p_line, i_width, p_region, -1, -1);
911 DrawBlack( p_line, i_width, p_region, -1, 1);
912 DrawBlack( p_line, i_width, p_region, 1, -1);
913 DrawBlack( p_line, i_width, p_region, 1, 1);
915 DrawBlack( p_line, i_width, p_region, -2, 0);
916 DrawBlack( p_line, i_width, p_region, 0, -2);
917 DrawBlack( p_line, i_width, p_region, 2, 0);
918 DrawBlack( p_line, i_width, p_region, 0, 2);
920 DrawBlack( p_line, i_width, p_region, -2, -2);
921 DrawBlack( p_line, i_width, p_region, -2, 2);
922 DrawBlack( p_line, i_width, p_region, 2, -2);
923 DrawBlack( p_line, i_width, p_region, 2, 2);
925 DrawBlack( p_line, i_width, p_region, -3, 0);
926 DrawBlack( p_line, i_width, p_region, 0, -3);
927 DrawBlack( p_line, i_width, p_region, 3, 0);
928 DrawBlack( p_line, i_width, p_region, 0, 3);
931 for( ; p_line != NULL; p_line = p_line->p_next )
933 int i_glyph_tmax = 0;
934 int i_bitmap_offset, i_offset, i_align_offset = 0;
935 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
937 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
938 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
941 if( p_line->i_width < i_width )
943 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
945 i_align_offset = i_width - p_line->i_width;
947 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
949 i_align_offset = ( i_width - p_line->i_width ) / 2;
953 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
955 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
957 i_offset = ( p_line->p_glyph_pos[ i ].y +
958 i_glyph_tmax - p_glyph->top + 3 ) *
959 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
960 i_align_offset;
962 if( p_line->b_new_color_mode )
964 /* Every glyph can (and in fact must) have its own color */
965 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
968 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
970 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
972 uint8_t i_y_local = i_y;
973 uint8_t i_u_local = i_u;
974 uint8_t i_v_local = i_v;
976 if( p_line->p_fg_bg_ratio != 0x00 )
978 int i_split = p_glyph->bitmap.width *
979 p_line->p_fg_bg_ratio[ i ] / 0x7f;
981 if( x > i_split )
983 YUVFromRGB( p_line->p_bg_rgb[ i ],
984 &i_y_local, &i_u_local, &i_v_local );
988 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
990 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
991 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
993 p_dst_u[i_offset+x] = i_u;
994 p_dst_v[i_offset+x] = i_v;
996 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
997 p_dst_a[i_offset+x] = 0xff;
1000 i_offset += i_pitch;
1003 if( p_line->pi_underline_thickness[ i ] )
1005 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1006 p_line->pi_underline_offset[ i ],
1007 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1008 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1009 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1010 i_glyph_tmax, i_align_offset,
1011 i_y, i_u, i_v,
1012 p_region);
1017 /* Apply the alpha setting */
1018 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1019 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1021 return VLC_SUCCESS;
1025 * This function renders a text subpicture region into another one.
1026 * It also calculates the size needed for this string, and renders the
1027 * needed glyphs into memory. It is used as pf_add_string callback in
1028 * the vout method by this module
1030 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1031 subpicture_region_t *p_region_in )
1033 filter_sys_t *p_sys = p_filter->p_sys;
1034 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1035 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1036 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1037 int i_string_length;
1038 char *psz_string;
1039 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1040 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1041 vlc_value_t val;
1042 int i_scale = 1000;
1044 FT_BBox line;
1045 FT_BBox glyph_size;
1046 FT_Vector result;
1047 FT_Glyph tmp_glyph;
1049 /* Sanity check */
1050 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1051 psz_string = p_region_in->psz_text;
1052 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1054 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1055 i_scale = val.i_int;
1057 if( p_region_in->p_style )
1059 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1060 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1061 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1063 else
1065 i_font_color = p_sys->i_font_color;
1066 i_font_alpha = 255 - p_sys->i_font_opacity;
1067 i_font_size = p_sys->i_default_font_size * i_scale / 1000;
1070 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1071 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1072 SetFontSize( p_filter, i_font_size );
1074 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
1075 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
1076 i_blue = i_font_color & 0x000000FF;
1078 result.x = result.y = 0;
1079 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1081 psz_unicode = psz_unicode_orig =
1082 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1083 if( psz_unicode == NULL )
1084 goto error;
1085 #if defined(WORDS_BIGENDIAN)
1086 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1087 #else
1088 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1089 #endif
1090 if( iconv_handle == (vlc_iconv_t)-1 )
1092 msg_Warn( p_filter, "unable to do conversion" );
1093 goto error;
1097 char *p_out_buffer;
1098 const char *p_in_buffer = psz_string;
1099 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1100 i_in_bytes = strlen( psz_string );
1101 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1102 i_out_bytes_left = i_out_bytes;
1103 p_out_buffer = (char *)psz_unicode;
1104 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1105 &i_in_bytes,
1106 &p_out_buffer, &i_out_bytes_left );
1108 vlc_iconv_close( iconv_handle );
1110 if( i_in_bytes )
1112 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1113 "bytes left %u", (unsigned)i_in_bytes );
1114 goto error;
1116 *(uint32_t*)p_out_buffer = 0;
1117 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1120 #if defined(HAVE_FRIBIDI)
1122 uint32_t *p_fribidi_string;
1123 int32_t start_pos, pos = 0;
1125 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1126 if( !p_fribidi_string )
1127 goto error;
1129 /* Do bidi conversion line-by-line */
1130 while( pos < i_string_length )
1132 while( pos < i_string_length )
1134 i_char = psz_unicode[pos];
1135 if (i_char != '\r' && i_char != '\n')
1136 break;
1137 p_fribidi_string[pos] = i_char;
1138 ++pos;
1140 start_pos = pos;
1141 while( pos < i_string_length )
1143 i_char = psz_unicode[pos];
1144 if (i_char == '\r' || i_char == '\n')
1145 break;
1146 ++pos;
1148 if (pos > start_pos)
1150 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1151 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1152 pos - start_pos,
1153 &base_dir,
1154 (FriBidiChar*)p_fribidi_string + start_pos,
1155 0, 0, 0);
1159 free( psz_unicode_orig );
1160 psz_unicode = psz_unicode_orig = p_fribidi_string;
1161 p_fribidi_string[ i_string_length ] = 0;
1163 #endif
1165 /* Calculate relative glyph positions and a bounding box for the
1166 * entire string */
1167 if( !(p_line = NewLine( strlen( psz_string ))) )
1168 goto error;
1169 p_lines = p_line;
1170 i_pen_x = i_pen_y = 0;
1171 i_previous = i = 0;
1172 psz_line_start = psz_unicode;
1174 #define face p_sys->p_face
1175 #define glyph face->glyph
1177 while( *psz_unicode )
1179 i_char = *psz_unicode++;
1180 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1182 continue;
1185 if( i_char == '\n' )
1187 psz_line_start = psz_unicode;
1188 if( !(p_next = NewLine( strlen( psz_string ))) )
1189 goto error;
1190 p_line->p_next = p_next;
1191 p_line->i_width = line.xMax;
1192 p_line->i_height = face->size->metrics.height >> 6;
1193 p_line->pp_glyphs[ i ] = NULL;
1194 p_line->i_alpha = i_font_alpha;
1195 p_line->i_red = i_red;
1196 p_line->i_green = i_green;
1197 p_line->i_blue = i_blue;
1198 p_prev = p_line;
1199 p_line = p_next;
1200 result.x = __MAX( result.x, line.xMax );
1201 result.y += face->size->metrics.height >> 6;
1202 i_pen_x = 0;
1203 i_previous = i = 0;
1204 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1205 i_pen_y += face->size->metrics.height >> 6;
1206 #if 0
1207 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1208 #endif
1209 continue;
1212 i_glyph_index = FT_Get_Char_Index( face, i_char );
1213 if( p_sys->i_use_kerning && i_glyph_index
1214 && i_previous )
1216 FT_Vector delta;
1217 FT_Get_Kerning( face, i_previous, i_glyph_index,
1218 ft_kerning_default, &delta );
1219 i_pen_x += delta.x >> 6;
1222 p_line->p_glyph_pos[ i ].x = i_pen_x;
1223 p_line->p_glyph_pos[ i ].y = i_pen_y;
1224 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1225 if( i_error )
1227 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1228 if( i_error )
1230 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1231 " %d", i_error );
1232 goto error;
1235 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1236 if( i_error )
1238 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1239 "%d", i_error );
1240 goto error;
1242 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1243 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1244 if( i_error )
1246 FT_Done_Glyph( tmp_glyph );
1247 continue;
1249 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1251 /* Do rest */
1252 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1253 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1254 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1256 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1257 p_line->pp_glyphs[ i ] = NULL;
1258 FreeLine( p_line );
1259 p_line = NewLine( strlen( psz_string ));
1260 if( p_prev ) p_prev->p_next = p_line;
1261 else p_lines = p_line;
1263 uint32_t *psz_unicode_saved = psz_unicode;
1264 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1266 psz_unicode--;
1268 if( psz_unicode == psz_line_start )
1269 { /* try harder to break that line */
1270 psz_unicode = psz_unicode_saved;
1271 while( psz_unicode > psz_line_start &&
1272 *psz_unicode != '_' && *psz_unicode != '/' &&
1273 *psz_unicode != '\\' && *psz_unicode != '.' )
1275 psz_unicode--;
1278 if( psz_unicode == psz_line_start )
1280 msg_Warn( p_filter, "unbreakable string" );
1281 goto error;
1283 else
1285 *psz_unicode = '\n';
1287 psz_unicode = psz_line_start;
1288 i_pen_x = 0;
1289 i_previous = i = 0;
1290 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1291 continue;
1293 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1294 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1296 i_previous = i_glyph_index;
1297 i_pen_x += glyph->advance.x >> 6;
1298 i++;
1301 p_line->i_width = line.xMax;
1302 p_line->i_height = face->size->metrics.height >> 6;
1303 p_line->pp_glyphs[ i ] = NULL;
1304 p_line->i_alpha = i_font_alpha;
1305 p_line->i_red = i_red;
1306 p_line->i_green = i_green;
1307 p_line->i_blue = i_blue;
1308 result.x = __MAX( result.x, line.xMax );
1309 result.y += line.yMax - line.yMin;
1311 #undef face
1312 #undef glyph
1314 p_region_out->i_x = p_region_in->i_x;
1315 p_region_out->i_y = p_region_in->i_y;
1317 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1318 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1319 else
1320 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1322 free( psz_unicode_orig );
1323 FreeLines( p_lines );
1324 return VLC_SUCCESS;
1326 error:
1327 free( psz_unicode_orig );
1328 FreeLines( p_lines );
1329 return VLC_EGENERIC;
1332 #ifdef HAVE_FONTCONFIG
1333 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1334 uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
1335 bool b_italic, bool b_uline, bool b_through )
1337 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1339 if( p_style )
1341 p_style->i_font_size = i_font_size;
1342 p_style->i_font_color = i_font_color;
1343 p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1344 p_style->b_italic = b_italic;
1345 p_style->b_bold = b_bold;
1346 p_style->b_underline = b_uline;
1347 p_style->b_through = b_through;
1349 p_style->psz_fontname = strdup( psz_fontname );
1351 return p_style;
1354 static void DeleteStyle( ft_style_t *p_style )
1356 if( p_style )
1358 free( p_style->psz_fontname );
1359 free( p_style );
1363 static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1365 if( !s1 || !s2 )
1366 return false;
1367 if( s1 == s2 )
1368 return true;
1370 if(( s1->i_font_size == s2->i_font_size ) &&
1371 ( s1->i_font_color == s2->i_font_color ) &&
1372 ( s1->b_italic == s2->b_italic ) &&
1373 ( s1->b_through == s2->b_through ) &&
1374 ( s1->b_bold == s2->b_bold ) &&
1375 ( s1->b_underline == s2->b_underline ) &&
1376 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1378 return true;
1380 return false;
1383 static void IconvText( filter_t *p_filter, const char *psz_string,
1384 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1386 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1388 /* If memory hasn't been allocated for our output string, allocate it here
1389 * - the calling function must now be responsible for freeing it.
1391 if( !*ppsz_unicode )
1392 *ppsz_unicode = (uint32_t *)
1393 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1395 /* We don't need to handle a NULL pointer in *ppsz_unicode
1396 * if we are instead testing for a non NULL value like we are here */
1398 if( *ppsz_unicode )
1400 #if defined(WORDS_BIGENDIAN)
1401 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1402 #else
1403 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1404 #endif
1405 if( iconv_handle != (vlc_iconv_t)-1 )
1407 char *p_in_buffer, *p_out_buffer;
1408 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1409 i_in_bytes = strlen( psz_string );
1410 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1411 i_out_bytes_left = i_out_bytes;
1412 p_in_buffer = (char *) psz_string;
1413 p_out_buffer = (char *) *ppsz_unicode;
1414 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1415 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1417 vlc_iconv_close( iconv_handle );
1419 if( i_in_bytes )
1421 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1422 "bytes left %u", (unsigned)i_in_bytes );
1424 else
1426 *(uint32_t*)p_out_buffer = 0;
1427 *i_string_length =
1428 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1431 else
1433 msg_Warn( p_filter, "unable to do conversion" );
1438 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1439 font_stack_t **p_fonts, bool b_bold, bool b_italic,
1440 bool b_uline, bool b_through )
1442 ft_style_t *p_style = NULL;
1444 char *psz_fontname = NULL;
1445 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1446 uint32_t i_karaoke_bg_color = i_font_color;
1447 int i_font_size = p_sys->i_font_size;
1449 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1450 &i_font_color, &i_karaoke_bg_color ))
1452 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1453 i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
1455 return p_style;
1458 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1459 bool b_uline, bool b_through, bool b_bold,
1460 bool b_italic, int i_karaoke_bgcolor,
1461 line_desc_t *p_line, uint32_t *psz_unicode,
1462 int *pi_pen_x, int i_pen_y, int *pi_start,
1463 FT_Vector *p_result )
1465 FT_BBox line;
1466 int i_yMin, i_yMax;
1467 int i;
1468 bool b_first_on_line = true;
1470 int i_previous = 0;
1471 int i_pen_x_start = *pi_pen_x;
1473 uint32_t *psz_unicode_start = psz_unicode;
1475 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1477 /* Account for part of line already in position */
1478 for( i=0; i<*pi_start; i++ )
1480 FT_BBox glyph_size;
1482 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1483 ft_glyph_bbox_pixels, &glyph_size );
1485 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1486 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1487 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1488 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1490 i_yMin = line.yMin;
1491 i_yMax = line.yMax;
1493 if( line.xMax > 0 )
1494 b_first_on_line = false;
1496 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1498 FT_BBox glyph_size;
1499 FT_Glyph tmp_glyph;
1500 int i_error;
1502 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1503 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1504 && i_previous )
1506 FT_Vector delta;
1507 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1508 ft_kerning_default, &delta );
1509 *pi_pen_x += delta.x >> 6;
1511 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1512 p_line->p_glyph_pos[ i ].y = i_pen_y;
1514 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
1515 if( i_error )
1517 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1518 if( i_error )
1520 msg_Err( p_filter,
1521 "unable to render text FT_Load_Glyph returned %d", i_error );
1522 p_line->pp_glyphs[ i ] = NULL;
1523 return VLC_EGENERIC;
1527 /* Do synthetic styling now that Freetype supports it;
1528 * ie. if the font we have loaded is NOT already in the
1529 * style that the tags want, then switch it on; if they
1530 * are then don't. */
1531 if (b_bold && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ))
1532 FT_GlyphSlot_Embolden( p_face->glyph );
1533 if (b_italic && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ))
1534 FT_GlyphSlot_Oblique( p_face->glyph );
1536 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1537 if( i_error )
1539 msg_Err( p_filter,
1540 "unable to render text FT_Get_Glyph returned %d", i_error );
1541 p_line->pp_glyphs[ i ] = NULL;
1542 return VLC_EGENERIC;
1544 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1545 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1546 if( i_error )
1548 FT_Done_Glyph( tmp_glyph );
1549 continue;
1551 if( b_uline || b_through )
1553 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1554 p_face->size->metrics.y_scale));
1555 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1556 p_face->size->metrics.y_scale));
1558 p_line->pi_underline_offset[ i ] =
1559 ( aOffset < 0 ) ? -aOffset : aOffset;
1560 p_line->pi_underline_thickness[ i ] =
1561 ( aSize < 0 ) ? -aSize : aSize;
1562 if (b_through)
1564 /* Move the baseline to make it strikethrough instead of
1565 * underline. That means that strikethrough takes precedence
1567 float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
1568 p_face->size->metrics.y_scale));
1570 p_line->pi_underline_offset[ i ] -=
1571 ( aDescent < 0 ) ? -aDescent : aDescent;
1575 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1576 p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1577 p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1578 p_line->p_fg_bg_ratio[ i ] = 0x00;
1580 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1581 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1582 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1584 for( ; i >= *pi_start; i-- )
1585 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1586 i = *pi_start;
1588 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1590 psz_unicode--;
1592 if( psz_unicode == psz_unicode_start )
1594 if( b_first_on_line )
1596 msg_Warn( p_filter, "unbreakable string" );
1597 p_line->pp_glyphs[ i ] = NULL;
1598 return VLC_EGENERIC;
1600 *pi_pen_x = i_pen_x_start;
1602 p_line->i_width = line.xMax;
1603 p_line->i_height = __MAX( p_line->i_height,
1604 p_face->size->metrics.height >> 6 );
1605 p_line->pp_glyphs[ i ] = NULL;
1607 p_result->x = __MAX( p_result->x, line.xMax );
1608 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1609 i_yMax - i_yMin ) );
1610 return VLC_SUCCESS;
1612 else
1614 *psz_unicode = '\n';
1616 psz_unicode = psz_unicode_start;
1617 *pi_pen_x = i_pen_x_start;
1618 i_previous = 0;
1620 line.yMax = i_yMax;
1621 line.yMin = i_yMin;
1623 continue;
1625 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1626 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1628 i_previous = i_glyph_index;
1629 *pi_pen_x += p_face->glyph->advance.x >> 6;
1630 i++;
1632 p_line->i_width = line.xMax;
1633 p_line->i_height = __MAX( p_line->i_height,
1634 p_face->size->metrics.height >> 6 );
1635 p_line->pp_glyphs[ i ] = NULL;
1637 p_result->x = __MAX( p_result->x, line.xMax );
1638 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1639 line.yMax - line.yMin ) );
1641 *pi_start = i;
1643 /* Get rid of any text processed - if necessary repositioning
1644 * at the start of a new line of text
1646 if( !*psz_unicode )
1648 *psz_unicode_start = '\0';
1650 else if( psz_unicode > psz_unicode_start )
1652 for( i=0; psz_unicode[ i ]; i++ )
1653 psz_unicode_start[ i ] = psz_unicode[ i ];
1654 psz_unicode_start[ i ] = '\0';
1657 return VLC_SUCCESS;
1660 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1661 uint32_t **psz_text_out, uint32_t *pi_runs,
1662 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1663 ft_style_t *p_style )
1665 uint32_t i_string_length = 0;
1667 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1668 *psz_text_out += i_string_length;
1670 if( ppp_styles && ppi_run_lengths )
1672 (*pi_runs)++;
1674 /* XXX this logic looks somewhat broken */
1676 if( *ppp_styles )
1678 *ppp_styles = realloc_or_free( *ppp_styles,
1679 *pi_runs * sizeof( ft_style_t * ) );
1681 else if( *pi_runs == 1 )
1683 *ppp_styles = malloc( *pi_runs * sizeof( ft_style_t * ) );
1686 /* We have just malloc'ed this memory successfully -
1687 * *pi_runs HAS to be within the memory area of *ppp_styles */
1688 if( *ppp_styles )
1690 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1691 p_style = NULL;
1694 /* XXX more iffy logic */
1696 if( *ppi_run_lengths )
1698 *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
1699 *pi_runs * sizeof( uint32_t ) );
1701 else if( *pi_runs == 1 )
1703 *ppi_run_lengths = (uint32_t *)
1704 malloc( *pi_runs * sizeof( uint32_t ) );
1707 /* same remarks here */
1708 if( *ppi_run_lengths )
1710 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1713 /* If we couldn't use the p_style argument due to memory allocation
1714 * problems above, release it here.
1716 if( p_style ) DeleteStyle( p_style );
1719 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
1721 int k;
1723 for( k=0; k < p_sys->i_font_attachments; k++ )
1725 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1726 int i_font_idx = 0;
1727 FT_Face p_face = NULL;
1729 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1730 p_attach->p_data,
1731 p_attach->i_data,
1732 i_font_idx,
1733 &p_face ))
1735 if( p_face )
1737 bool match = !strcasecmp( p_face->family_name,
1738 p_style->psz_fontname );
1740 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
1741 match = match && p_style->b_bold;
1742 else
1743 match = match && !p_style->b_bold;
1745 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
1746 match = match && p_style->b_italic;
1747 else
1748 match = match && !p_style->b_italic;
1750 if( match )
1752 *pp_face = p_face;
1753 return VLC_SUCCESS;
1756 FT_Done_Face( p_face );
1758 i_font_idx++;
1761 return VLC_EGENERIC;
1764 static int ProcessLines( filter_t *p_filter,
1765 uint32_t *psz_text,
1766 int i_len,
1768 uint32_t i_runs,
1769 uint32_t *pi_run_lengths,
1770 ft_style_t **pp_styles,
1771 line_desc_t **pp_lines,
1773 FT_Vector *p_result,
1775 bool b_karaoke,
1776 uint32_t i_k_runs,
1777 uint32_t *pi_k_run_lengths,
1778 uint32_t *pi_k_durations )
1780 filter_sys_t *p_sys = p_filter->p_sys;
1781 ft_style_t **pp_char_styles;
1782 int *p_new_positions = NULL;
1783 int8_t *p_levels = NULL;
1784 uint8_t *pi_karaoke_bar = NULL;
1785 uint32_t i, j, k;
1786 int i_prev;
1788 /* Assign each character in the text string its style explicitly, so that
1789 * after the characters have been shuffled around by Fribidi, we can re-apply
1790 * the styles, and to simplify the calculation of runs within a line.
1792 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1793 if( !pp_char_styles )
1794 return VLC_ENOMEM;
1796 if( b_karaoke )
1798 pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
1799 /* If we can't allocate sufficient memory for karaoke, continue anyway -
1800 * we just won't be able to display the progress bar; at least we'll
1801 * get the text.
1805 i = 0;
1806 for( j = 0; j < i_runs; j++ )
1807 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1808 pp_char_styles[ i++ ] = pp_styles[ j ];
1810 #if defined(HAVE_FRIBIDI)
1812 ft_style_t **pp_char_styles_new;
1813 int *p_old_positions;
1814 uint32_t *p_fribidi_string;
1815 int start_pos, pos = 0;
1817 pp_char_styles_new = (ft_style_t **)
1818 malloc( i_len * sizeof( ft_style_t * ));
1820 p_fribidi_string = (uint32_t *)
1821 malloc( (i_len + 1) * sizeof(uint32_t) );
1822 p_old_positions = (int *)
1823 malloc( (i_len + 1) * sizeof( int ) );
1824 p_new_positions = (int *)
1825 malloc( (i_len + 1) * sizeof( int ) );
1826 p_levels = (int8_t *)
1827 malloc( (i_len + 1) * sizeof( int8_t ) );
1829 if( ! pp_char_styles_new ||
1830 ! p_fribidi_string ||
1831 ! p_old_positions ||
1832 ! p_new_positions ||
1833 ! p_levels )
1835 free( p_levels );
1836 free( p_old_positions );
1837 free( p_new_positions );
1838 free( p_fribidi_string );
1839 free( pp_char_styles_new );
1840 free( pi_karaoke_bar );
1842 free( pp_char_styles );
1843 return VLC_ENOMEM;
1846 /* Do bidi conversion line-by-line */
1847 while(pos < i_len)
1849 while(pos < i_len) {
1850 if (psz_text[pos] != '\n')
1851 break;
1852 p_fribidi_string[pos] = psz_text[pos];
1853 pp_char_styles_new[pos] = pp_char_styles[pos];
1854 p_new_positions[pos] = pos;
1855 p_levels[pos] = 0;
1856 ++pos;
1858 start_pos = pos;
1859 while(pos < i_len) {
1860 if (psz_text[pos] == '\n')
1861 break;
1862 ++pos;
1864 if (pos > start_pos)
1866 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1867 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1868 pos - start_pos, &base_dir,
1869 (FriBidiChar*)p_fribidi_string + start_pos,
1870 p_new_positions + start_pos,
1871 p_old_positions,
1872 p_levels + start_pos );
1873 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1875 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1876 p_old_positions[ j - start_pos ] ];
1877 p_new_positions[ j ] += start_pos;
1881 free( p_old_positions );
1882 free( pp_char_styles );
1883 pp_char_styles = pp_char_styles_new;
1884 psz_text = p_fribidi_string;
1885 p_fribidi_string[ i_len ] = 0;
1887 #endif
1888 /* Work out the karaoke */
1889 if( pi_karaoke_bar )
1891 int64_t i_last_duration = 0;
1892 int64_t i_duration = 0;
1893 int64_t i_start_pos = 0;
1894 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1896 for( k = 0; k< i_k_runs; k++ )
1898 double fraction = 0.0;
1900 i_duration += pi_k_durations[ k ];
1902 if( i_duration < i_elapsed )
1904 /* Completely finished this run-length -
1905 * let it render normally */
1907 fraction = 1.0;
1909 else if( i_elapsed < i_last_duration )
1911 /* Haven't got up to this segment yet -
1912 * render it completely in karaoke BG mode */
1914 fraction = 0.0;
1916 else
1918 /* Partway through this run */
1920 fraction = (double)(i_elapsed - i_last_duration) /
1921 (double)pi_k_durations[ k ];
1923 for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
1925 double shade = pi_k_run_lengths[ k ] * fraction;
1927 if( p_new_positions )
1928 j = p_new_positions[ i_start_pos + i ];
1929 else
1930 j = i_start_pos + i;
1932 if( i < (uint32_t)shade )
1933 pi_karaoke_bar[ j ] = 0xff;
1934 else if( (double)i > shade )
1935 pi_karaoke_bar[ j ] = 0x00;
1936 else
1938 shade -= (int)shade;
1939 pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
1940 ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
1944 i_last_duration = i_duration;
1945 i_start_pos += pi_k_run_lengths[ k ];
1948 free( p_levels );
1949 free( p_new_positions );
1951 FT_Vector tmp_result;
1953 line_desc_t *p_line = NULL;
1954 line_desc_t *p_prev = NULL;
1956 int i_pen_x = 0;
1957 int i_pen_y = 0;
1958 int i_posn = 0;
1960 p_result->x = p_result->y = 0;
1961 tmp_result.x = tmp_result.y = 0;
1963 i_prev = 0;
1964 for( k = 0; k <= (uint32_t) i_len; k++ )
1966 if( ( k == (uint32_t) i_len ) ||
1967 ( ( k > 0 ) &&
1968 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1970 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1972 /* End of the current style run */
1973 FT_Face p_face = NULL;
1974 int i_idx = 0;
1976 /* Look for a match amongst our attachments first */
1977 CheckForEmbeddedFont( p_sys, &p_face, p_style );
1979 if( ! p_face )
1981 char *psz_fontfile = NULL;
1983 psz_fontfile = FontConfig_Select( NULL,
1984 p_style->psz_fontname,
1985 p_style->b_bold,
1986 p_style->b_italic,
1987 &i_idx );
1988 if( psz_fontfile && ! *psz_fontfile )
1990 msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
1991 " so using default font", p_style->psz_fontname,
1992 ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
1993 (p_style->b_bold ? "(Bold)" :
1994 (p_style->b_italic ? "(Italic)" : ""))) );
1995 free( psz_fontfile );
1996 psz_fontfile = NULL;
1999 if( psz_fontfile )
2001 if( FT_New_Face( p_sys->p_library,
2002 psz_fontfile, i_idx, &p_face ) )
2004 free( psz_fontfile );
2005 free( pp_char_styles );
2006 #if defined(HAVE_FRIBIDI)
2007 free( psz_text );
2008 #endif
2009 free( pi_karaoke_bar );
2010 return VLC_EGENERIC;
2012 free( psz_fontfile );
2015 if( p_face &&
2016 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2018 /* We've loaded a font face which is unhelpful for actually
2019 * rendering text - fallback to the default one.
2021 FT_Done_Face( p_face );
2022 p_face = NULL;
2025 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2026 ft_encoding_unicode ) ||
2027 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2028 p_style->i_font_size ) )
2030 if( p_face ) FT_Done_Face( p_face );
2031 free( pp_char_styles );
2032 #if defined(HAVE_FRIBIDI)
2033 free( psz_text );
2034 #endif
2035 free( pi_karaoke_bar );
2036 return VLC_EGENERIC;
2038 p_sys->i_use_kerning =
2039 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
2042 uint32_t *psz_unicode = (uint32_t *)
2043 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2044 if( !psz_unicode )
2046 if( p_face ) FT_Done_Face( p_face );
2047 free( pp_char_styles );
2048 free( psz_unicode );
2049 #if defined(HAVE_FRIBIDI)
2050 free( psz_text );
2051 #endif
2052 free( pi_karaoke_bar );
2053 return VLC_ENOMEM;
2055 memcpy( psz_unicode, psz_text + i_prev,
2056 sizeof( uint32_t ) * ( k - i_prev ) );
2057 psz_unicode[ k - i_prev ] = 0;
2058 while( *psz_unicode )
2060 if( !p_line )
2062 if( !(p_line = NewLine( i_len - i_prev)) )
2064 if( p_face ) FT_Done_Face( p_face );
2065 free( pp_char_styles );
2066 free( psz_unicode );
2067 #if defined(HAVE_FRIBIDI)
2068 free( psz_text );
2069 #endif
2070 free( pi_karaoke_bar );
2071 return VLC_ENOMEM;
2073 /* New Color mode only works in YUVA rendering mode --
2074 * (RGB mode has palette constraints on it). We therefore
2075 * need to populate the legacy colour fields also.
2077 p_line->b_new_color_mode = true;
2078 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2079 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2080 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2081 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2082 p_line->p_next = NULL;
2083 i_pen_x = 0;
2084 i_pen_y += tmp_result.y;
2085 tmp_result.x = 0;
2086 tmp_result.y = 0;
2087 i_posn = 0;
2088 if( p_prev ) p_prev->p_next = p_line;
2089 else *pp_lines = p_line;
2092 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2093 p_style->i_font_color, p_style->b_underline,
2094 p_style->b_through,
2095 p_style->b_bold,
2096 p_style->b_italic,
2097 p_style->i_karaoke_bg_color,
2098 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2099 &tmp_result ) != VLC_SUCCESS )
2101 if( p_face ) FT_Done_Face( p_face );
2102 free( pp_char_styles );
2103 free( psz_unicode );
2104 #if defined(HAVE_FRIBIDI)
2105 free( psz_text );
2106 #endif
2107 free( pi_karaoke_bar );
2108 return VLC_EGENERIC;
2111 if( *psz_unicode )
2113 p_result->x = __MAX( p_result->x, tmp_result.x );
2114 p_result->y += tmp_result.y;
2116 p_prev = p_line;
2117 p_line = NULL;
2119 if( *psz_unicode == '\n')
2121 uint32_t *c_ptr;
2123 for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2125 *c_ptr = *(c_ptr+1);
2130 free( psz_unicode );
2131 if( p_face ) FT_Done_Face( p_face );
2132 i_prev = k;
2135 free( pp_char_styles );
2136 #if defined(HAVE_FRIBIDI)
2137 free( psz_text );
2138 #endif
2139 if( p_line )
2141 p_result->x = __MAX( p_result->x, tmp_result.x );
2142 p_result->y += tmp_result.y;
2145 if( pi_karaoke_bar )
2147 int i = 0;
2148 for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2150 for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2152 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2154 /* do nothing */
2156 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2158 /* 100% BG colour will render faster if we
2159 * instead make it 100% FG colour, so leave
2160 * the ratio alone and copy the value across
2162 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2164 else
2166 if( pi_karaoke_bar[ i ] & 0x80 )
2168 /* Swap Left and Right sides over for Right aligned
2169 * language text (eg. Arabic, Hebrew)
2171 uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2173 p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2174 p_line->p_bg_rgb[ k ] = i_tmp;
2176 p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2179 /* Jump over the '\n' at the line-end */
2180 i++;
2182 free( pi_karaoke_bar );
2185 return VLC_SUCCESS;
2188 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2189 subpicture_region_t *p_region_in )
2191 int rv = VLC_SUCCESS;
2192 stream_t *p_sub = NULL;
2194 if( !p_region_in || !p_region_in->psz_html )
2195 return VLC_EGENERIC;
2197 /* Reset the default fontsize in case screen metrics have changed */
2198 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2200 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2201 (uint8_t *) p_region_in->psz_html,
2202 strlen( p_region_in->psz_html ),
2203 true );
2204 if( unlikely(p_sub == NULL) )
2205 return VLC_SUCCESS;
2207 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2208 bool b_karaoke = false;
2210 if( !p_xml_reader )
2211 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2212 else
2213 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2215 p_filter->p_sys->p_xml = p_xml_reader;
2216 if( p_xml_reader )
2218 /* Look for Root Node */
2219 if( xml_ReaderRead( p_xml_reader ) == 1 )
2221 char *psz_node = xml_ReaderName( p_xml_reader );
2223 if( !strcasecmp( "karaoke", psz_node ) )
2225 /* We're going to have to render the text a number
2226 * of times to show the progress marker on the text.
2228 var_SetBool( p_filter, "text-rerender", true );
2229 b_karaoke = true;
2231 else if( !strcasecmp( "text", psz_node ) )
2233 b_karaoke = false;
2235 else
2237 /* Only text and karaoke tags are supported */
2238 msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2239 xml_ReaderDelete( p_xml_reader );
2240 p_xml_reader = NULL;
2241 rv = VLC_EGENERIC;
2244 free( psz_node );
2248 if( p_xml_reader )
2250 uint32_t *psz_text;
2251 int i_len = 0;
2252 uint32_t i_runs = 0;
2253 uint32_t i_k_runs = 0;
2254 uint32_t *pi_run_lengths = NULL;
2255 uint32_t *pi_k_run_lengths = NULL;
2256 uint32_t *pi_k_durations = NULL;
2257 ft_style_t **pp_styles = NULL;
2258 FT_Vector result;
2259 line_desc_t *p_lines = NULL;
2261 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2262 sizeof( uint32_t ) );
2263 if( psz_text )
2265 rv = ProcessNodes( p_filter, p_xml_reader,
2266 p_region_in->p_style, psz_text, &i_len,
2267 &i_runs, &pi_run_lengths, &pp_styles,
2268 b_karaoke, &i_k_runs, &pi_k_run_lengths,
2269 &pi_k_durations );
2271 p_region_out->i_x = p_region_in->i_x;
2272 p_region_out->i_y = p_region_in->i_y;
2274 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2276 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2277 pi_run_lengths, pp_styles, &p_lines,
2278 &result, b_karaoke, i_k_runs,
2279 pi_k_run_lengths, pi_k_durations );
2282 for( uint_fast32_t k=0; k<i_runs; k++)
2283 DeleteStyle( pp_styles[k] );
2284 free( pp_styles );
2285 free( pi_run_lengths );
2286 free( psz_text );
2288 /* Don't attempt to render text that couldn't be layed out
2289 * properly. */
2290 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2292 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2293 Render( p_filter, p_region_out, p_lines,
2294 result.x, result.y );
2295 else
2296 RenderYUVA( p_filter, p_region_out, p_lines,
2297 result.x, result.y );
2300 xml_ReaderReset( p_xml_reader, NULL );
2301 FreeLines( p_lines );
2303 stream_Delete( p_sub );
2304 return rv;
2307 static char* FontConfig_Select( FcConfig* priv, const char* family,
2308 bool b_bold, bool b_italic, int *i_idx )
2310 FcResult result;
2311 FcPattern *pat, *p_pat;
2312 FcChar8* val_s;
2313 FcBool val_b;
2315 pat = FcPatternCreate();
2316 if (!pat) return NULL;
2318 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2319 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2320 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
2321 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
2323 FcDefaultSubstitute( pat );
2325 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2327 FcPatternDestroy( pat );
2328 return NULL;
2331 p_pat = FcFontMatch( priv, pat, &result );
2332 FcPatternDestroy( pat );
2333 if( !p_pat ) return NULL;
2335 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2336 || ( val_b != FcTrue ) )
2338 FcPatternDestroy( p_pat );
2339 return NULL;
2341 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2343 *i_idx = 0;
2346 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2348 FcPatternDestroy( p_pat );
2349 return NULL;
2353 if( strcasecmp((const char*)val_s, family ) != 0 )
2354 msg_Warn( p_filter, "fontconfig: selected font family is not"
2355 "the requested one: '%s' != '%s'\n",
2356 (const char*)val_s, family );
2359 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2361 FcPatternDestroy( p_pat );
2362 return NULL;
2365 FcPatternDestroy( p_pat );
2366 return strdup( (const char*)val_s );
2368 #else
2370 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
2371 uint32_t **psz_text_out, uint32_t *pi_runs,
2372 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
2373 ft_style_t *p_style )
2375 VLC_UNUSED(p_filter);
2376 VLC_UNUSED(psz_text_in);
2377 VLC_UNUSED(psz_text_out);
2378 VLC_UNUSED(pi_runs);
2379 VLC_UNUSED(ppi_run_lengths);
2380 VLC_UNUSED(ppp_styles);
2381 VLC_UNUSED(p_style);
2384 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
2385 font_stack_t **p_fonts, bool b_bold, bool b_italic,
2386 bool b_uline, bool b_through )
2388 VLC_UNUSED(p_sys);
2389 VLC_UNUSED(p_fonts);
2390 VLC_UNUSED(b_bold);
2391 VLC_UNUSED(b_italic);
2392 VLC_UNUSED(b_uline);
2393 VLC_UNUSED(b_through);
2394 return NULL;
2396 #endif
2398 static void FreeLine( line_desc_t *p_line )
2400 unsigned int i;
2401 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2403 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2405 free( p_line->pp_glyphs );
2406 free( p_line->p_glyph_pos );
2407 free( p_line->p_fg_rgb );
2408 free( p_line->p_bg_rgb );
2409 free( p_line->p_fg_bg_ratio );
2410 free( p_line->pi_underline_offset );
2411 free( p_line->pi_underline_thickness );
2412 free( p_line );
2415 static void FreeLines( line_desc_t *p_lines )
2417 line_desc_t *p_line, *p_next;
2419 if( !p_lines ) return;
2421 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2423 p_next = p_line->p_next;
2424 FreeLine( p_line );
2428 static line_desc_t *NewLine( int i_count )
2430 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2432 if( !p_line ) return NULL;
2433 p_line->i_height = 0;
2434 p_line->i_width = 0;
2435 p_line->p_next = NULL;
2437 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2438 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2439 p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2440 p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2441 p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2442 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
2443 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2444 if( ( p_line->pp_glyphs == NULL ) ||
2445 ( p_line->p_glyph_pos == NULL ) ||
2446 ( p_line->p_fg_rgb == NULL ) ||
2447 ( p_line->p_bg_rgb == NULL ) ||
2448 ( p_line->p_fg_bg_ratio == NULL ) ||
2449 ( p_line->pi_underline_offset == NULL ) ||
2450 ( p_line->pi_underline_thickness == NULL ) )
2452 free( p_line->pi_underline_thickness );
2453 free( p_line->pi_underline_offset );
2454 free( p_line->p_fg_rgb );
2455 free( p_line->p_bg_rgb );
2456 free( p_line->p_fg_bg_ratio );
2457 free( p_line->p_glyph_pos );
2458 free( p_line->pp_glyphs );
2459 free( p_line );
2460 return NULL;
2462 p_line->pp_glyphs[0] = NULL;
2463 p_line->b_new_color_mode = false;
2465 return p_line;
2468 static int GetFontSize( filter_t *p_filter )
2470 filter_sys_t *p_sys = p_filter->p_sys;
2471 vlc_value_t val;
2472 int i_size = 0;
2474 if( p_sys->i_default_font_size )
2476 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2477 i_size = p_sys->i_default_font_size * val.i_int / 1000;
2478 else
2479 i_size = p_sys->i_default_font_size;
2481 else
2483 var_Get( p_filter, "freetype-rel-fontsize", &val );
2484 if( val.i_int > 0 )
2486 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2487 p_filter->p_sys->i_display_height =
2488 p_filter->fmt_out.video.i_height;
2491 if( i_size <= 0 )
2493 msg_Warn( p_filter, "invalid fontsize, using 12" );
2494 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2495 i_size = 12 * val.i_int / 1000;
2496 else
2497 i_size = 12;
2499 return i_size;
2502 static int SetFontSize( filter_t *p_filter, int i_size )
2504 filter_sys_t *p_sys = p_filter->p_sys;
2506 if( !i_size )
2508 i_size = GetFontSize( p_filter );
2510 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2513 p_sys->i_font_size = i_size;
2515 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2517 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2518 return VLC_EGENERIC;
2521 return VLC_SUCCESS;
2524 static void YUVFromRGB( uint32_t i_argb,
2525 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2527 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
2528 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
2529 int i_blue = ( i_argb & 0x000000ff );
2531 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
2532 802 * i_blue + 4096 + 131072 ) >> 13, 235);
2533 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
2534 3598 * i_blue + 4096 + 1048576) >> 13, 240);
2535 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2536 -585 * i_blue + 4096 + 1048576) >> 13, 240);