1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include <vlc_stream.h>
39 #include <vlc_input.h>
40 #include <vlc_strings.h>
41 #include <vlc_dialog.h>
42 #include <vlc_memory.h>
47 #include <freetype/ftsynth.h>
48 #include FT_FREETYPE_H
50 #define FT_FLOOR(X) ((X & -64) >> 6)
51 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
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"
69 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
70 #define FC_DEFAULT_FONT "Serif Bold"
73 #if defined(HAVE_FRIBIDI)
74 #include <fribidi/fribidi.h>
77 #ifdef HAVE_FONTCONFIG
78 #include <fontconfig/fontconfig.h>
80 #define DEFAULT_FONT FC_DEFAULT_FONT
85 /*****************************************************************************
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")
96 #define FONT_LONGTEXT N_("Font file for the font you want to use")
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") };
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
,
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
)
182 /*****************************************************************************
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 *,
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
;
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. */
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
;
225 int i_red
, i_green
, i_blue
;
230 static line_desc_t
*NewLine( int );
235 uint32_t i_font_color
; /* ARGB */
236 uint32_t i_karaoke_bg_color
; /* ARGB */
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 *****************************************************************************/
256 FT_Library p_library
; /* handle to library */
257 FT_Face p_face
; /* handle to face object */
259 uint8_t i_font_opacity
;
264 int i_default_font_size
;
265 int i_display_height
;
266 #ifdef HAVE_FONTCONFIG
267 char* psz_fontfamily
;
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
;
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
;
303 /* Allocate structure */
304 p_filter
->p_sys
= p_sys
= malloc( sizeof( filter_sys_t
) );
307 #ifdef HAVE_FONTCONFIG
308 p_sys
->psz_fontfamily
= NULL
;
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 );
328 if( !psz_fontfamily
|| !*psz_fontfamily
)
330 free( psz_fontfamily
);
331 #ifdef HAVE_FONTCONFIG
332 psz_fontfamily
=strdup( DEFAULT_FONT
);
334 psz_fontfamily
= (char *)malloc( PATH_MAX
+ 1 );
335 if( !psz_fontfamily
)
338 GetWindowsDirectory( psz_fontfamily
, PATH_MAX
+ 1 );
339 strcat( psz_fontfamily
, "\\fonts\\arial.ttf" );
341 strcpy( psz_fontfamily
, DEFAULT_FONT
);
343 msg_Err( p_filter
,"User didn't specify fontfile, using %s", psz_fontfamily
);
347 #ifdef HAVE_FONTCONFIG
348 msg_Dbg( p_filter
, "Building font databases.");
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
);
362 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
364 FcConfigBuildFonts( fcConfig
);
366 msg_Dbg( p_filter
, "Took %ld microseconds", (long)((t2
- t1
)) );
370 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
371 dialog_ProgressDestroy( p_dialog
);
375 /* Lets find some fontfile from freetype-font variable family */
377 if( asprintf( &psz_fontsize
, "%d", p_sys
->i_default_font_size
) == -1 )
380 fontpattern
= FcPatternCreate();
383 msg_Err( p_filter
, "Creating fontpattern failed");
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");
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");
408 FcPatternGetString( fontmatch
, FC_FILE
, 0, &psz_fontfile
);
409 FcPatternGetInteger( fontmatch
, FC_INDEX
, 0, &fontindex
);
412 msg_Err( p_filter
, "Failed to get fontfile");
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
);
422 psz_fontfile
= psz_fontfamily
;
426 i_error
= FT_Init_FreeType( &p_sys
->p_library
);
429 msg_Err( p_filter
, "couldn't initialize freetype" );
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)" );
444 msg_Err( p_filter
, "failed to load font file %s",
445 psz_fontfile
? psz_fontfile
: "(null)" );
449 i_error
= FT_Select_Charmap( p_sys
->p_face
, ft_encoding_unicode
);
452 msg_Err( p_filter
, "font has no unicode translation table" );
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
);
470 p_filter
->pf_render_html
= NULL
;
473 free( psz_fontfamily
);
474 LoadFontsFromAttachments( p_filter
);
479 #ifdef HAVE_FONTCONFIG
480 if( fontmatch
) FcPatternDestroy( fontmatch
);
481 if( fontpattern
) FcPatternDestroy( fontpattern
);
486 dialog_ProgressDestroy( p_dialog
);
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
);
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
)
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
);
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
);
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
) )
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
)
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
;
560 vlc_input_attachment_Delete( p_attach
);
563 free( pp_attachments
);
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};
583 int i
, x
, y
, i_pitch
;
584 uint8_t i_y
; /* YUV values, derived from incoming RGB */
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
)
602 fmt
.p_palette
= p_region
->fmt
.p_palette
? p_region
->fmt
.p_palette
: malloc(sizeof(*fmt
.p_palette
));
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;
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 +
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
] )
677 ((int)p_glyph
->bitmap
.buffer
[i_bitmap_offset
] + 8)/16;
684 /* Outlining (find something better than nearest neighbour filtering ?) */
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
;
697 for( x
= 1; x
< (int)fmt
.i_width
- 1; 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;
705 memset( p_top
, 0, fmt
.i_width
);
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
)
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
;
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
++ )
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);
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
] )
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
] )
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;
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
;
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
]) )
829 ((int)p_glyph
->bitmap
.buffer
[i_bitmap_offset
]);
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
;
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 )
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
)
870 /* Calculate text color components */
871 YUVFromRGB( (p_line
->i_red
<< 16) |
872 (p_line
->i_green
<< 8) |
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
);
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 +
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;
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
,
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;
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
;
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
;
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;
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
)
1085 #if defined(WORDS_BIGENDIAN)
1086 iconv_handle
= vlc_iconv_open( "UCS-4BE", "UTF-8" );
1088 iconv_handle
= vlc_iconv_open( "UCS-4LE", "UTF-8" );
1090 if( iconv_handle
== (vlc_iconv_t
)-1 )
1092 msg_Warn( p_filter
, "unable to do conversion" );
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
,
1106 &p_out_buffer
, &i_out_bytes_left
);
1108 vlc_iconv_close( iconv_handle
);
1112 msg_Warn( p_filter
, "failed to convert string to unicode (%m), "
1113 "bytes left %u", (unsigned)i_in_bytes
);
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
)
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')
1137 p_fribidi_string
[pos
] = i_char
;
1141 while( pos
< i_string_length
)
1143 i_char
= psz_unicode
[pos
];
1144 if (i_char
== '\r' || i_char
== '\n')
1148 if (pos
> start_pos
)
1150 FriBidiCharType base_dir
= FRIBIDI_TYPE_LTR
;
1151 fribidi_log2vis((FriBidiChar
*)psz_unicode
+ start_pos
,
1154 (FriBidiChar
*)p_fribidi_string
+ start_pos
,
1159 free( psz_unicode_orig
);
1160 psz_unicode
= psz_unicode_orig
= p_fribidi_string
;
1161 p_fribidi_string
[ i_string_length
] = 0;
1165 /* Calculate relative glyph positions and a bounding box for the
1167 if( !(p_line
= NewLine( strlen( psz_string
))) )
1170 i_pen_x
= i_pen_y
= 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 */
1185 if( i_char
== '\n' )
1187 psz_line_start
= psz_unicode
;
1188 if( !(p_next
= NewLine( strlen( psz_string
))) )
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
;
1200 result
.x
= __MAX( result
.x
, line
.xMax
);
1201 result
.y
+= face
->size
->metrics
.height
>> 6;
1204 line
.xMin
= line
.xMax
= line
.yMin
= line
.yMax
= 0;
1205 i_pen_y
+= face
->size
->metrics
.height
>> 6;
1207 msg_Dbg( p_filter
, "Creating new line, i is %d", i
);
1212 i_glyph_index
= FT_Get_Char_Index( face
, i_char
);
1213 if( p_sys
->i_use_kerning
&& i_glyph_index
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
);
1227 i_error
= FT_Load_Glyph( face
, i_glyph_index
, FT_LOAD_DEFAULT
);
1230 msg_Err( p_filter
, "unable to render text FT_Load_Glyph returned"
1235 i_error
= FT_Get_Glyph( glyph
, &tmp_glyph
);
1238 msg_Err( p_filter
, "unable to render text FT_Get_Glyph returned "
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);
1246 FT_Done_Glyph( tmp_glyph
);
1249 p_line
->pp_glyphs
[ i
] = (FT_BitmapGlyph
)tmp_glyph
;
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
;
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
!= ' ' )
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
!= '.' )
1278 if( psz_unicode
== psz_line_start
)
1280 msg_Warn( p_filter
, "unbreakable string" );
1285 *psz_unicode
= '\n';
1287 psz_unicode
= psz_line_start
;
1290 line
.xMin
= line
.xMax
= line
.yMin
= line
.yMax
= 0;
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;
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
;
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
);
1320 RenderYUVA( p_filter
, p_region_out
, p_lines
, result
.x
, result
.y
);
1322 free( psz_unicode_orig
);
1323 FreeLines( p_lines
);
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
));
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
);
1354 static void DeleteStyle( ft_style_t
*p_style
)
1358 free( p_style
->psz_fontname
);
1363 static bool StyleEquals( ft_style_t
*s1
, ft_style_t
*s2
)
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
)))
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 */
1400 #if defined(WORDS_BIGENDIAN)
1401 iconv_handle
= vlc_iconv_open( "UCS-4BE", "UTF-8" );
1403 iconv_handle
= vlc_iconv_open( "UCS-4LE", "UTF-8" );
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
);
1421 msg_Warn( p_filter
, "failed to convert string to unicode (%m), "
1422 "bytes left %u", (unsigned)i_in_bytes
);
1426 *(uint32_t*)p_out_buffer
= 0;
1428 (i_out_bytes
- i_out_bytes_left
) / sizeof(uint32_t);
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
);
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
)
1468 bool b_first_on_line
= true;
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
++ )
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
);
1494 b_first_on_line
= false;
1496 while( *psz_unicode
&& ( *psz_unicode
!= '\n' ) )
1502 int i_glyph_index
= FT_Get_Char_Index( p_face
, *psz_unicode
++ );
1503 if( FT_HAS_KERNING( p_face
) && i_glyph_index
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
);
1517 i_error
= FT_Load_Glyph( p_face
, i_glyph_index
, FT_LOAD_DEFAULT
);
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
);
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);
1548 FT_Done_Glyph( tmp_glyph
);
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
;
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
] );
1588 while( psz_unicode
> psz_unicode_start
&& *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
) );
1614 *psz_unicode
= '\n';
1616 psz_unicode
= psz_unicode_start
;
1617 *pi_pen_x
= i_pen_x_start
;
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;
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
) );
1643 /* Get rid of any text processed - if necessary repositioning
1644 * at the start of a new line of text
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';
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
)
1674 /* XXX this logic looks somewhat broken */
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 */
1690 (*ppp_styles
)[ *pi_runs
- 1 ] = p_style
;
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
)
1723 for( k
=0; k
< p_sys
->i_font_attachments
; k
++ )
1725 input_attachment_t
*p_attach
= p_sys
->pp_font_attachments
[k
];
1727 FT_Face p_face
= NULL
;
1729 while( 0 == FT_New_Memory_Face( p_sys
->p_library
,
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
;
1743 match
= match
&& !p_style
->b_bold
;
1745 if( p_face
->style_flags
& FT_STYLE_FLAG_ITALIC
)
1746 match
= match
&& p_style
->b_italic
;
1748 match
= match
&& !p_style
->b_italic
;
1756 FT_Done_Face( p_face
);
1761 return VLC_EGENERIC
;
1764 static int ProcessLines( filter_t
*p_filter
,
1769 uint32_t *pi_run_lengths
,
1770 ft_style_t
**pp_styles
,
1771 line_desc_t
**pp_lines
,
1773 FT_Vector
*p_result
,
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
;
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
)
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
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
||
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
);
1846 /* Do bidi conversion line-by-line */
1849 while(pos
< i_len
) {
1850 if (psz_text
[pos
] != '\n')
1852 p_fribidi_string
[pos
] = psz_text
[pos
];
1853 pp_char_styles_new
[pos
] = pp_char_styles
[pos
];
1854 p_new_positions
[pos
] = pos
;
1859 while(pos
< i_len
) {
1860 if (psz_text
[pos
] == '\n')
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
,
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;
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 */
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 */
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
];
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;
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
];
1949 free( p_new_positions
);
1951 FT_Vector tmp_result
;
1953 line_desc_t
*p_line
= NULL
;
1954 line_desc_t
*p_prev
= NULL
;
1960 p_result
->x
= p_result
->y
= 0;
1961 tmp_result
.x
= tmp_result
.y
= 0;
1964 for( k
= 0; k
<= (uint32_t) i_len
; k
++ )
1966 if( ( k
== (uint32_t) i_len
) ||
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
;
1976 /* Look for a match amongst our attachments first */
1977 CheckForEmbeddedFont( p_sys
, &p_face
, p_style
);
1981 char *psz_fontfile
= NULL
;
1983 psz_fontfile
= FontConfig_Select( NULL
,
1984 p_style
->psz_fontname
,
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
;
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)
2009 free( pi_karaoke_bar
);
2010 return VLC_EGENERIC
;
2012 free( psz_fontfile
);
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
);
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)
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 ));
2046 if( p_face
) FT_Done_Face( p_face
);
2047 free( pp_char_styles
);
2048 free( psz_unicode
);
2049 #if defined(HAVE_FRIBIDI)
2052 free( pi_karaoke_bar
);
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
)
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)
2070 free( pi_karaoke_bar
);
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
;
2084 i_pen_y
+= tmp_result
.y
;
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
,
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)
2107 free( pi_karaoke_bar
);
2108 return VLC_EGENERIC
;
2113 p_result
->x
= __MAX( p_result
->x
, tmp_result
.x
);
2114 p_result
->y
+= tmp_result
.y
;
2119 if( *psz_unicode
== '\n')
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
);
2135 free( pp_char_styles
);
2136 #if defined(HAVE_FRIBIDI)
2141 p_result
->x
= __MAX( p_result
->x
, tmp_result
.x
);
2142 p_result
->y
+= tmp_result
.y
;
2145 if( pi_karaoke_bar
)
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)
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
];
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 */
2182 free( pi_karaoke_bar
);
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
),
2204 if( unlikely(p_sub
== NULL
) )
2207 xml_reader_t
*p_xml_reader
= p_filter
->p_sys
->p_xml
;
2208 bool b_karaoke
= false;
2211 p_xml_reader
= xml_ReaderCreate( p_filter
, p_sub
);
2213 p_xml_reader
= xml_ReaderReset( p_xml_reader
, p_sub
);
2215 p_filter
->p_sys
->p_xml
= 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 );
2231 else if( !strcasecmp( "text", psz_node
) )
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
;
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
;
2259 line_desc_t
*p_lines
= NULL
;
2261 psz_text
= (uint32_t *)malloc( strlen( p_region_in
->psz_html
) *
2262 sizeof( uint32_t ) );
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
,
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
] );
2285 free( pi_run_lengths
);
2288 /* Don't attempt to render text that couldn't be layed out
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
);
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
);
2307 static char* FontConfig_Select( FcConfig
* priv
, const char* family
,
2308 bool b_bold
, bool b_italic
, int *i_idx
)
2311 FcPattern
*pat
, *p_pat
;
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
);
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
);
2341 if( FcResultMatch
!= FcPatternGetInteger( p_pat
, FC_INDEX
, 0, i_idx
) )
2346 if( FcResultMatch
!= FcPatternGetString( p_pat
, FC_FAMILY
, 0, &val_s
) )
2348 FcPatternDestroy( p_pat
);
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
);
2365 FcPatternDestroy( p_pat
);
2366 return strdup( (const char*)val_s
);
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
)
2389 VLC_UNUSED(p_fonts
);
2391 VLC_UNUSED(b_italic
);
2392 VLC_UNUSED(b_uline
);
2393 VLC_UNUSED(b_through
);
2398 static void FreeLine( line_desc_t
*p_line
)
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
);
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
;
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
);
2462 p_line
->pp_glyphs
[0] = NULL
;
2463 p_line
->b_new_color_mode
= false;
2468 static int GetFontSize( filter_t
*p_filter
)
2470 filter_sys_t
*p_sys
= p_filter
->p_sys
;
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;
2479 i_size
= p_sys
->i_default_font_size
;
2483 var_Get( p_filter
, "freetype-rel-fontsize", &val
);
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
;
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;
2502 static int SetFontSize( filter_t
*p_filter
, int i_size
)
2504 filter_sys_t
*p_sys
= p_filter
->p_sys
;
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
;
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);