A little more detail regarding using my github copies of the code with where it's...
[vlc/adversarial.git] / modules / text_renderer / freetype.c
blobe562afc8735bc682cf084c09e1c1f8cd5bcfac28
1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <bitmap@videolan.org>
10 * Jean-Baptiste Kempf <jb@videolan.org>
11 * Felix Paul Kühne <fkuehne@videolan.org>
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU Lesser General Public License as published by
15 * the Free Software Foundation; either version 2.1 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public License
24 * along with this program; if not, write to the Free Software Foundation, Inc.,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
28 /*****************************************************************************
29 * Preamble
30 *****************************************************************************/
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35 #include <math.h>
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_stream.h> /* stream_MemoryNew */
40 #include <vlc_input.h> /* vlc_input_attachment_* */
41 #include <vlc_xml.h> /* xml_reader */
42 #include <vlc_dialog.h> /* FcCache dialog */
43 #include <vlc_filter.h> /* filter_sys_t */
44 #include <vlc_text_style.h> /* text_style_t*/
46 /* Freetype */
47 #include <ft2build.h>
48 #include FT_FREETYPE_H
49 #include FT_GLYPH_H
50 #include FT_STROKER_H
51 #include FT_SYNTHESIS_H
53 #define FT_FLOOR(X) ((X & -64) >> 6)
54 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
55 #ifndef FT_MulFix
56 #define FT_MulFix(v, s) (((v)*(s))>>16)
57 #endif
59 /* RTL */
60 #if defined(HAVE_FRIBIDI)
61 # include <fribidi/fribidi.h>
62 #endif
64 /* apple stuff */
65 #ifdef __APPLE__
66 # include <TargetConditionals.h>
67 # undef HAVE_FONTCONFIG
68 # define HAVE_GET_FONT_BY_FAMILY_NAME
69 #endif
71 /* Win32 */
72 #ifdef _WIN32
73 # undef HAVE_FONTCONFIG
74 # if !VLC_WINSTORE_APP
75 # define HAVE_GET_FONT_BY_FAMILY_NAME
76 # endif
77 #endif
79 /* FontConfig */
80 #ifdef HAVE_FONTCONFIG
81 # define HAVE_GET_FONT_BY_FAMILY_NAME
82 #endif
84 #include <assert.h>
86 #include "text_renderer.h"
87 #include "platform_fonts.h"
89 /*****************************************************************************
90 * Module descriptor
91 *****************************************************************************/
92 static int Create ( vlc_object_t * );
93 static void Destroy( vlc_object_t * );
95 #define FONT_TEXT N_("Font")
96 #define MONOSPACE_FONT_TEXT N_("Monospace Font")
98 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
99 #define FONT_LONGTEXT N_("Font file for the font you want to use")
101 #define FONTSIZE_TEXT N_("Font size in pixels")
102 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
103 "that will be rendered on the video. " \
104 "If set to something different than 0 this option will override the " \
105 "relative font size." )
106 #define OPACITY_TEXT N_("Text opacity")
107 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
108 "text that will be rendered on the video. 0 = transparent, " \
109 "255 = totally opaque. " )
110 #define COLOR_TEXT N_("Text default color")
111 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
112 "the video. This must be an hexadecimal (like HTML colors). The first two "\
113 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
114 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
115 #define FONTSIZER_TEXT N_("Relative font size")
116 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
117 "fonts that will be rendered on the video. If absolute font size is set, "\
118 "relative size will be overridden." )
119 #define BOLD_TEXT N_("Force bold")
121 #define BG_OPACITY_TEXT N_("Background opacity")
122 #define BG_COLOR_TEXT N_("Background color")
124 #define OUTLINE_OPACITY_TEXT N_("Outline opacity")
125 #define OUTLINE_COLOR_TEXT N_("Outline color")
126 #define OUTLINE_THICKNESS_TEXT N_("Outline thickness")
128 #define SHADOW_OPACITY_TEXT N_("Shadow opacity")
129 #define SHADOW_COLOR_TEXT N_("Shadow color")
130 #define SHADOW_ANGLE_TEXT N_("Shadow angle")
131 #define SHADOW_DISTANCE_TEXT N_("Shadow distance")
134 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
135 static const char *const ppsz_sizes_text[] = {
136 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
137 #define YUVP_TEXT N_("Use YUVP renderer")
138 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
139 "This option is only needed if you want to encode into DVB subtitles" )
141 static const int pi_color_values[] = {
142 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
143 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
144 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
146 static const char *const ppsz_color_descriptions[] = {
147 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
148 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
149 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
151 static const int pi_outline_thickness[] = {
152 0, 2, 4, 6,
154 static const char *const ppsz_outline_thickness[] = {
155 N_("None"), N_("Thin"), N_("Normal"), N_("Thick"),
158 vlc_module_begin ()
159 set_shortname( N_("Text renderer"))
160 set_description( N_("Freetype2 font renderer") )
161 set_category( CAT_VIDEO )
162 set_subcategory( SUBCAT_VIDEO_SUBPIC )
164 #ifdef HAVE_GET_FONT_BY_FAMILY_NAME
165 add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
166 add_font( "freetype-monofont", DEFAULT_MONOSPACE_FAMILY, MONOSPACE_FONT_TEXT, FAMILY_LONGTEXT, false )
167 #else
168 add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
169 add_loadfile( "freetype-monofont", DEFAULT_MONOSPACE_FONT_FILE, MONOSPACE_FONT_TEXT, FONT_LONGTEXT, false )
170 #endif
172 add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
173 FONTSIZE_LONGTEXT, true )
174 change_integer_range( 0, 4096)
175 change_safe()
177 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
178 FONTSIZER_LONGTEXT, false )
179 change_integer_list( pi_sizes, ppsz_sizes_text )
180 change_safe()
182 /* opacity valid on 0..255, with default 255 = fully opaque */
183 add_integer_with_range( "freetype-opacity", 255, 0, 255,
184 OPACITY_TEXT, OPACITY_LONGTEXT, false )
185 change_safe()
187 /* hook to the color values list, with default 0x00ffffff = white */
188 add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
189 COLOR_LONGTEXT, false )
190 change_integer_list( pi_color_values, ppsz_color_descriptions )
191 change_safe()
193 add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
194 change_safe()
196 add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
197 BG_OPACITY_TEXT, NULL, false )
198 change_safe()
199 add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
200 NULL, false )
201 change_integer_list( pi_color_values, ppsz_color_descriptions )
202 change_safe()
204 add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
205 OUTLINE_OPACITY_TEXT, NULL, false )
206 change_safe()
207 add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
208 NULL, false )
209 change_integer_list( pi_color_values, ppsz_color_descriptions )
210 change_safe()
211 add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
212 NULL, false )
213 change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
214 change_safe()
216 add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
217 SHADOW_OPACITY_TEXT, NULL, false )
218 change_safe()
219 add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
220 NULL, false )
221 change_integer_list( pi_color_values, ppsz_color_descriptions )
222 change_safe()
223 add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
224 SHADOW_ANGLE_TEXT, NULL, false )
225 change_safe()
226 add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
227 SHADOW_DISTANCE_TEXT, NULL, false )
228 change_safe()
230 add_obsolete_integer( "freetype-effect" );
232 add_bool( "freetype-yuvp", false, YUVP_TEXT,
233 YUVP_LONGTEXT, true )
234 set_capability( "text renderer", 100 )
235 add_shortcut( "text" )
236 set_callbacks( Create, Destroy )
237 vlc_module_end ()
240 /*****************************************************************************
241 * Local prototypes
242 *****************************************************************************/
244 typedef struct
246 FT_BitmapGlyph p_glyph;
247 FT_BitmapGlyph p_outline;
248 FT_BitmapGlyph p_shadow;
249 uint32_t i_color; /* ARGB color */
250 int i_line_offset; /* underline/strikethrough offset */
251 int i_line_thickness; /* underline/strikethrough thickness */
252 } line_character_t;
254 typedef struct line_desc_t line_desc_t;
255 struct line_desc_t
257 line_desc_t *p_next;
259 int i_width;
260 int i_height;
261 int i_base_line;
262 int i_character_count;
263 line_character_t *p_character;
266 /*****************************************************************************
267 * filter_sys_t: freetype local data
268 *****************************************************************************
269 * This structure is part of the video output thread descriptor.
270 * It describes the freetype specific properties of an output thread.
271 *****************************************************************************/
272 struct filter_sys_t
274 FT_Library p_library; /* handle to library */
275 FT_Face p_face; /* handle to face object */
276 FT_Stroker p_stroker; /* handle to path stroker object */
278 xml_reader_t *p_xml; /* vlc xml parser */
280 text_style_t style; /* Current Style */
282 /* More styles... */
283 float f_shadow_vector_x;
284 float f_shadow_vector_y;
285 int i_default_font_size;
287 /* Attachments */
288 input_attachment_t **pp_font_attachments;
289 int i_font_attachments;
291 char * (*pf_select) (filter_t *, const char* family,
292 bool bold, bool italic, int size,
293 int *index);
297 /* */
298 static void YUVFromRGB( uint32_t i_argb,
299 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
301 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
302 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
303 int i_blue = ( i_argb & 0x000000ff );
305 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
306 802 * i_blue + 4096 + 131072 ) >> 13, 235);
307 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
308 3598 * i_blue + 4096 + 1048576) >> 13, 240);
309 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
310 -585 * i_blue + 4096 + 1048576) >> 13, 240);
312 static void RGBFromRGB( uint32_t i_argb,
313 uint8_t *pi_r, uint8_t *pi_g, uint8_t *pi_b )
315 *pi_r = ( i_argb & 0x00ff0000 ) >> 16;
316 *pi_g = ( i_argb & 0x0000ff00 ) >> 8;
317 *pi_b = ( i_argb & 0x000000ff );
320 /*****************************************************************************
321 * Make any TTF/OTF fonts present in the attachments of the media file
322 * and store them for later use by the FreeType Engine
323 *****************************************************************************/
324 static int LoadFontsFromAttachments( filter_t *p_filter )
326 filter_sys_t *p_sys = p_filter->p_sys;
327 input_attachment_t **pp_attachments;
328 int i_attachments_cnt;
330 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
331 return VLC_EGENERIC;
333 p_sys->i_font_attachments = 0;
334 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
335 if( !p_sys->pp_font_attachments )
336 return VLC_ENOMEM;
338 for( int k = 0; k < i_attachments_cnt; k++ )
340 input_attachment_t *p_attach = pp_attachments[k];
342 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
343 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
344 p_attach->i_data > 0 && p_attach->p_data )
346 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
348 else
350 vlc_input_attachment_Delete( p_attach );
353 free( pp_attachments );
355 return VLC_SUCCESS;
358 static int GetFontSize( filter_t *p_filter )
360 filter_sys_t *p_sys = p_filter->p_sys;
361 int i_size = 0;
363 if( p_sys->i_default_font_size )
365 i_size = p_sys->i_default_font_size;
367 else
369 int i_ratio = var_InheritInteger( p_filter, "freetype-rel-fontsize" );
370 if( i_ratio > 0 )
372 i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
375 if( i_size <= 0 )
377 msg_Warn( p_filter, "invalid fontsize, using 12" );
378 i_size = 12;
380 return i_size;
383 static int SetFontSize( filter_t *p_filter, int i_size )
385 filter_sys_t *p_sys = p_filter->p_sys;
387 if( !i_size )
389 i_size = GetFontSize( p_filter );
391 msg_Dbg( p_filter, "using fontsize: %i", i_size );
394 p_sys->style.i_font_size = i_size;
396 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
398 msg_Err( p_filter, "couldn't set font size to %d", i_size );
399 return VLC_EGENERIC;
402 return VLC_SUCCESS;
405 /*****************************************************************************
406 * RenderYUVP: place string in picture
407 *****************************************************************************
408 * This function merges the previously rendered freetype glyphs into a picture
409 *****************************************************************************/
410 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
411 line_desc_t *p_line,
412 FT_BBox *p_bbox )
414 VLC_UNUSED(p_filter);
415 static const uint8_t pi_gamma[16] =
416 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
417 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
419 uint8_t *p_dst;
420 video_format_t fmt;
421 int i, x, y, i_pitch;
422 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
424 /* Create a new subpicture region */
425 video_format_Init( &fmt, VLC_CODEC_YUVP );
426 fmt.i_width =
427 fmt.i_visible_width = p_bbox->xMax - p_bbox->xMin + 4;
428 fmt.i_height =
429 fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
431 assert( !p_region->p_picture );
432 p_region->p_picture = picture_NewFromFormat( &fmt );
433 if( !p_region->p_picture )
434 return VLC_EGENERIC;
435 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
436 p_region->fmt = fmt;
438 /* Calculate text color components
439 * Only use the first color */
440 int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
441 YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
443 /* Build palette */
444 fmt.p_palette->i_entries = 16;
445 for( i = 0; i < 8; i++ )
447 fmt.p_palette->palette[i][0] = 0;
448 fmt.p_palette->palette[i][1] = 0x80;
449 fmt.p_palette->palette[i][2] = 0x80;
450 fmt.p_palette->palette[i][3] = pi_gamma[i];
451 fmt.p_palette->palette[i][3] =
452 (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
454 for( i = 8; i < fmt.p_palette->i_entries; i++ )
456 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
457 fmt.p_palette->palette[i][1] = i_u;
458 fmt.p_palette->palette[i][2] = i_v;
459 fmt.p_palette->palette[i][3] = pi_gamma[i];
460 fmt.p_palette->palette[i][3] =
461 (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
464 p_dst = p_region->p_picture->Y_PIXELS;
465 i_pitch = p_region->p_picture->Y_PITCH;
467 /* Initialize the region pixels */
468 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
470 for( ; p_line != NULL; p_line = p_line->p_next )
472 int i_align_left = 0;
473 if( p_line->i_width < (int)fmt.i_visible_width )
475 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
476 i_align_left = ( fmt.i_visible_width - p_line->i_width );
477 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
478 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
480 int i_align_top = 0;
482 for( i = 0; i < p_line->i_character_count; i++ )
484 const line_character_t *ch = &p_line->p_character[i];
485 FT_BitmapGlyph p_glyph = ch->p_glyph;
487 int i_glyph_y = i_align_top - p_glyph->top + p_bbox->yMax + p_line->i_base_line;
488 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
490 for( y = 0; y < p_glyph->bitmap.rows; y++ )
492 for( x = 0; x < p_glyph->bitmap.width; x++ )
494 if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
495 p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
496 (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
502 /* Outlining (find something better than nearest neighbour filtering ?) */
503 if( 1 )
505 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
506 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
507 uint8_t left, current;
509 for( y = 1; y < (int)fmt.i_height - 1; y++ )
511 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
512 p_dst += p_region->p_picture->Y_PITCH;
513 left = 0;
515 for( x = 1; x < (int)fmt.i_width - 1; x++ )
517 current = p_dst[x];
518 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
519 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;
520 left = current;
523 memset( p_top, 0, fmt.i_width );
526 return VLC_SUCCESS;
529 /*****************************************************************************
530 * RenderYUVA: place string in picture
531 *****************************************************************************
532 * This function merges the previously rendered freetype glyphs into a picture
533 *****************************************************************************/
534 static void FillYUVAPicture( picture_t *p_picture,
535 int i_a, int i_y, int i_u, int i_v )
537 memset( p_picture->p[0].p_pixels, i_y,
538 p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
539 memset( p_picture->p[1].p_pixels, i_u,
540 p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
541 memset( p_picture->p[2].p_pixels, i_v,
542 p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
543 memset( p_picture->p[3].p_pixels, i_a,
544 p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
547 static inline void BlendYUVAPixel( picture_t *p_picture,
548 int i_picture_x, int i_picture_y,
549 int i_a, int i_y, int i_u, int i_v,
550 int i_alpha )
552 int i_an = i_a * i_alpha / 255;
554 uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
555 uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
556 uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
557 uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
559 int i_ao = *p_a;
560 if( i_ao == 0 )
562 *p_y = i_y;
563 *p_u = i_u;
564 *p_v = i_v;
565 *p_a = i_an;
567 else
569 *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
570 if( *p_a != 0 )
572 *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
573 *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
574 *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
579 static void FillRGBAPicture( picture_t *p_picture,
580 int i_a, int i_r, int i_g, int i_b )
582 for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
584 for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
586 uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
587 p_rgba[0] = i_r;
588 p_rgba[1] = i_g;
589 p_rgba[2] = i_b;
590 p_rgba[3] = i_a;
595 static inline void BlendRGBAPixel( picture_t *p_picture,
596 int i_picture_x, int i_picture_y,
597 int i_a, int i_r, int i_g, int i_b,
598 int i_alpha )
600 int i_an = i_a * i_alpha / 255;
602 uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
604 int i_ao = p_rgba[3];
605 if( i_ao == 0 )
607 p_rgba[0] = i_r;
608 p_rgba[1] = i_g;
609 p_rgba[2] = i_b;
610 p_rgba[3] = i_an;
612 else
614 p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
615 if( p_rgba[3] != 0 )
617 p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
618 p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
619 p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
624 static void FillARGBPicture(picture_t *pic, int a, int r, int g, int b)
626 if (a == 0)
627 r = g = b = 0;
628 if (a == r && a == b && a == g)
629 { /* fast path */
630 memset(pic->p->p_pixels, a, pic->p->i_visible_lines * pic->p->i_pitch);
631 return;
634 uint_fast32_t pixel = VLC_FOURCC(a, r, g, b);
635 uint8_t *line = pic->p->p_pixels;
637 for (unsigned lines = pic->p->i_visible_lines; lines > 0; lines--)
639 uint32_t *pixels = (uint32_t *)line;
640 for (unsigned cols = pic->p->i_visible_pitch; cols > 0; cols -= 4)
641 *(pixels++) = pixel;
642 line += pic->p->i_pitch;
646 static inline void BlendARGBPixel(picture_t *pic, int pic_x, int pic_y,
647 int a, int r, int g, int b, int alpha)
649 uint8_t *rgba = &pic->p->p_pixels[pic_y * pic->p->i_pitch + 4 * pic_x];
650 int an = a * alpha / 255;
651 int ao = rgba[3];
653 if (ao == 0)
655 rgba[0] = an;
656 rgba[1] = r;
657 rgba[2] = g;
658 rgba[3] = b;
660 else
662 rgba[0] = 255 - (255 - rgba[0]) * (255 - an) / 255;
663 if (rgba[0] != 0)
665 rgba[1] = (rgba[1] * ao * (255 - an) / 255 + r * an ) / rgba[0];
666 rgba[2] = (rgba[2] * ao * (255 - an) / 255 + g * an ) / rgba[0];
667 rgba[3] = (rgba[3] * ao * (255 - an) / 255 + b * an ) / rgba[0];
672 static inline void BlendAXYZGlyph( picture_t *p_picture,
673 int i_picture_x, int i_picture_y,
674 int i_a, int i_x, int i_y, int i_z,
675 FT_BitmapGlyph p_glyph,
676 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
679 for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
681 for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
682 BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
683 i_a, i_x, i_y, i_z,
684 p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
688 static inline void BlendAXYZLine( picture_t *p_picture,
689 int i_picture_x, int i_picture_y,
690 int i_a, int i_x, int i_y, int i_z,
691 const line_character_t *p_current,
692 const line_character_t *p_next,
693 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
695 int i_line_width = p_current->p_glyph->bitmap.width;
696 if( p_next )
697 i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
699 for( int dx = 0; dx < i_line_width; dx++ )
701 for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
702 BlendPixel( p_picture,
703 i_picture_x + dx,
704 i_picture_y + p_current->i_line_offset + dy,
705 i_a, i_x, i_y, i_z, 0xff );
709 static inline void RenderBackground( subpicture_region_t *p_region,
710 line_desc_t *p_line_head,
711 FT_BBox *p_bbox,
712 int i_margin,
713 picture_t *p_picture,
714 int i_text_width,
715 void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
716 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
718 for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
720 int i_align_left = i_margin;
721 int i_align_top = i_margin;
722 int line_start = 0;
723 int line_end = 0;
724 unsigned line_top = 0;
725 int line_bottom = 0;
726 int max_height = 0;
728 if( p_line->i_width < i_text_width )
730 /* Left offset to take into account alignment */
731 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
732 i_align_left += ( i_text_width - p_line->i_width );
733 else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
734 i_align_left = i_margin; /* Keep it the way it is */
735 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
736 i_align_left += ( i_text_width - p_line->i_width ) / 2;
739 /* Find the tallest character in the line */
740 for( int i = 0; i < p_line->i_character_count; i++ ) {
741 const line_character_t *ch = &p_line->p_character[i];
742 FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
743 if (p_glyph->top > max_height)
744 max_height = p_glyph->top;
747 /* Compute the background for the line (identify leading/trailing space) */
748 for( int i = 0; i < p_line->i_character_count; i++ ) {
749 const line_character_t *ch = &p_line->p_character[i];
750 FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
751 if (p_glyph && p_glyph->bitmap.rows > 0) {
752 // Found a non-whitespace character
753 line_start = i_align_left + p_glyph->left - p_bbox->xMin;
754 break;
758 /* Fudge factor to make sure caption background edges are left aligned
759 despite variable font width */
760 if (line_start < 12)
761 line_start = 0;
763 /* Find right boundary for bounding box for background */
764 for( int i = p_line->i_character_count; i > 0; i-- ) {
765 const line_character_t *ch = &p_line->p_character[i - 1];
766 FT_BitmapGlyph p_glyph = ch->p_shadow ? ch->p_shadow : ch->p_glyph;
767 if (p_glyph && p_glyph->bitmap.rows > 0) {
768 // Found a non-whitespace character
769 line_end = i_align_left + p_glyph->left - p_bbox->xMin + p_glyph->bitmap.width;
770 break;
774 /* Setup color for the background */
775 uint8_t i_x, i_y, i_z;
776 ExtractComponents( 0x000000, &i_x, &i_y, &i_z );
778 /* Compute the upper boundary for the background */
779 if ((i_align_top + p_line->i_base_line - max_height) < 0)
780 line_top = i_align_top + p_line->i_base_line;
781 else
782 line_top = i_align_top + p_line->i_base_line - max_height;
784 /* Compute lower boundary for the background */
785 line_bottom = __MIN(line_top + p_line->i_height, p_region->fmt.i_visible_height);
787 /* Render the actual background */
788 for( int dy = line_top; dy < line_bottom; dy++ )
790 for( int dx = line_start; dx < line_end; dx++ )
791 BlendPixel( p_picture, dx, dy, 0xff, i_x, i_y, i_z, 0xff );
796 static inline int RenderAXYZ( filter_t *p_filter,
797 subpicture_region_t *p_region,
798 line_desc_t *p_line_head,
799 FT_BBox *p_bbox,
800 int i_margin,
801 vlc_fourcc_t i_chroma,
802 void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
803 void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
804 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
806 filter_sys_t *p_sys = p_filter->p_sys;
808 /* Create a new subpicture region */
809 const int i_text_width = p_bbox->xMax - p_bbox->xMin;
810 const int i_text_height = p_bbox->yMax - p_bbox->yMin;
811 video_format_t fmt;
812 video_format_Init( &fmt, i_chroma );
813 fmt.i_width =
814 fmt.i_visible_width = i_text_width + 2 * i_margin;
815 fmt.i_height =
816 fmt.i_visible_height = i_text_height + 2 * i_margin;
818 picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
819 if( !p_region->p_picture )
820 return VLC_EGENERIC;
821 p_region->fmt = fmt;
823 /* Initialize the picture background */
824 uint8_t i_a = var_InheritInteger( p_filter, "freetype-background-opacity" );
825 i_a = VLC_CLIP( i_a, 0, 255 );
826 uint8_t i_x, i_y, i_z;
828 if (p_region->b_renderbg) {
829 /* Render the background just under the text */
830 FillPicture( p_picture, 0x00, 0x00, 0x00, 0x00 );
831 RenderBackground(p_region, p_line_head, p_bbox, i_margin, p_picture, i_text_width,
832 ExtractComponents, BlendPixel);
833 } else {
834 /* Render background under entire subpicture block */
835 int i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
836 i_background_color = VLC_CLIP( i_background_color, 0, 0xFFFFFF );
837 ExtractComponents( i_background_color, &i_x, &i_y, &i_z );
838 FillPicture( p_picture, i_a, i_x, i_y, i_z );
841 /* Render shadow then outline and then normal glyphs */
842 for( int g = 0; g < 3; g++ )
844 /* Render all lines */
845 for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
847 int i_align_left = i_margin;
848 if( p_line->i_width < i_text_width )
850 /* Left offset to take into account alignment */
851 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
852 i_align_left += ( i_text_width - p_line->i_width );
853 else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
854 i_align_left = i_margin; /* Keep it the way it is */
855 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
856 i_align_left += ( i_text_width - p_line->i_width ) / 2;
858 int i_align_top = i_margin;
860 /* Render all glyphs and underline/strikethrough */
861 for( int i = 0; i < p_line->i_character_count; i++ )
863 const line_character_t *ch = &p_line->p_character[i];
864 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
865 if( !p_glyph )
866 continue;
868 i_a = (ch->i_color >> 24) & 0xff;
869 uint32_t i_color;
870 switch (g) {
871 case 0:
872 i_a = i_a * p_sys->style.i_shadow_alpha / 255;
873 i_color = p_sys->style.i_shadow_color;
874 break;
875 case 1:
876 i_a = i_a * p_sys->style.i_outline_alpha / 255;
877 i_color = p_sys->style.i_outline_color;
878 break;
879 default:
880 i_color = ch->i_color;
881 break;
883 ExtractComponents( i_color, &i_x, &i_y, &i_z );
885 int i_glyph_y = i_align_top - p_glyph->top + p_bbox->yMax + p_line->i_base_line;
886 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
888 BlendAXYZGlyph( p_picture,
889 i_glyph_x, i_glyph_y,
890 i_a, i_x, i_y, i_z,
891 p_glyph,
892 BlendPixel );
894 /* underline/strikethrough are only rendered for the normal glyph */
895 if( g == 2 && ch->i_line_thickness > 0 )
896 BlendAXYZLine( p_picture,
897 i_glyph_x, i_glyph_y + p_glyph->top,
898 i_a, i_x, i_y, i_z,
899 &ch[0],
900 i + 1 < p_line->i_character_count ? &ch[1] : NULL,
901 BlendPixel );
906 return VLC_SUCCESS;
911 static void FreeLine( line_desc_t *p_line )
913 for( int i = 0; i < p_line->i_character_count; i++ )
915 line_character_t *ch = &p_line->p_character[i];
916 FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
917 if( ch->p_outline )
918 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
919 if( ch->p_shadow )
920 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
923 free( p_line->p_character );
924 free( p_line );
927 static void FreeLines( line_desc_t *p_lines )
929 for( line_desc_t *p_line = p_lines; p_line != NULL; )
931 line_desc_t *p_next = p_line->p_next;
932 FreeLine( p_line );
933 p_line = p_next;
937 static line_desc_t *NewLine( int i_count )
939 line_desc_t *p_line = malloc( sizeof(*p_line) );
941 if( !p_line )
942 return NULL;
944 p_line->p_next = NULL;
945 p_line->i_width = 0;
946 p_line->i_base_line = 0;
947 p_line->i_character_count = 0;
949 p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
950 if( !p_line->p_character )
952 free( p_line );
953 return NULL;
955 return p_line;
958 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
960 for( int k = 0; k < p_sys->i_font_attachments; k++ )
962 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
963 int i_font_idx = 0;
964 FT_Face p_face = NULL;
966 while( 0 == FT_New_Memory_Face( p_sys->p_library,
967 p_attach->p_data,
968 p_attach->i_data,
969 i_font_idx,
970 &p_face ))
972 if( p_face )
974 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD) ? STYLE_BOLD : 0) |
975 ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
976 if( p_face->family_name != NULL
977 && !strcasecmp( p_face->family_name, p_style->psz_fontname )
978 && (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC))
979 == i_style_received )
980 return p_face;
982 FT_Done_Face( p_face );
984 i_font_idx++;
987 return NULL;
990 static FT_Face LoadFace( filter_t *p_filter,
991 const text_style_t *p_style )
993 filter_sys_t *p_sys = p_filter->p_sys;
995 /* Look for a match amongst our attachments first */
996 FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
998 /* Load system wide font otheriwse */
999 if( !p_face )
1001 int i_idx = 0;
1002 char *psz_fontfile = NULL;
1003 if( p_sys->pf_select )
1004 psz_fontfile = p_sys->pf_select( p_filter,
1005 p_style->psz_fontname,
1006 (p_style->i_style_flags & STYLE_BOLD) != 0,
1007 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1009 &i_idx );
1010 else
1011 psz_fontfile = NULL;
1013 if( !psz_fontfile )
1014 return NULL;
1016 if( *psz_fontfile == '\0' )
1018 msg_Warn( p_filter,
1019 "We were not able to find a matching font: \"%s\" (%s %s),"
1020 " so using default font",
1021 p_style->psz_fontname,
1022 (p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
1023 (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1024 p_face = NULL;
1026 else
1028 if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1029 p_face = NULL;
1031 free( psz_fontfile );
1033 if( !p_face )
1034 return NULL;
1036 if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1038 /* We've loaded a font face which is unhelpful for actually
1039 * rendering text - fallback to the default one.
1041 FT_Done_Face( p_face );
1042 return NULL;
1044 return p_face;
1047 static int GetGlyph( filter_t *p_filter,
1048 FT_Glyph *pp_glyph, FT_BBox *p_glyph_bbox,
1049 FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
1050 FT_Glyph *pp_shadow, FT_BBox *p_shadow_bbox,
1052 FT_Face p_face,
1053 int i_glyph_index,
1054 int i_style_flags,
1055 FT_Vector *p_pen,
1056 FT_Vector *p_pen_shadow )
1058 if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
1059 FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
1061 msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
1062 return VLC_EGENERIC;
1065 /* Do synthetic styling now that Freetype supports it;
1066 * ie. if the font we have loaded is NOT already in the
1067 * style that the tags want, then switch it on; if they
1068 * are then don't. */
1069 if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1070 FT_GlyphSlot_Embolden( p_face->glyph );
1071 if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1072 FT_GlyphSlot_Oblique( p_face->glyph );
1074 FT_Glyph glyph;
1075 if( FT_Get_Glyph( p_face->glyph, &glyph ) )
1077 msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
1078 return VLC_EGENERIC;
1081 FT_Glyph outline = NULL;
1082 if( p_filter->p_sys->p_stroker )
1084 outline = glyph;
1085 if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
1086 outline = NULL;
1089 FT_Glyph shadow = NULL;
1090 if( p_filter->p_sys->style.i_shadow_alpha > 0 )
1092 shadow = outline ? outline : glyph;
1093 if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0 ) )
1095 shadow = NULL;
1097 else
1099 FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
1102 *pp_shadow = shadow;
1104 if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
1106 FT_Done_Glyph( glyph );
1107 if( outline )
1108 FT_Done_Glyph( outline );
1109 if( shadow )
1110 FT_Done_Glyph( shadow );
1111 return VLC_EGENERIC;
1113 FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
1114 *pp_glyph = glyph;
1116 if( outline )
1118 FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
1119 FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
1121 *pp_outline = outline;
1123 return VLC_SUCCESS;
1126 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
1128 FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
1129 if( p_bbox->xMin >= p_bbox->xMax )
1131 p_bbox->xMin = FT_CEIL(p_pen->x);
1132 p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
1133 glyph_bmp->left = p_bbox->xMin;
1135 if( p_bbox->yMin >= p_bbox->yMax )
1137 p_bbox->yMax = FT_CEIL(p_pen->y);
1138 p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
1139 glyph_bmp->top = p_bbox->yMax;
1143 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
1145 p_max->xMin = __MIN(p_max->xMin, p->xMin);
1146 p_max->yMin = __MIN(p_max->yMin, p->yMin);
1147 p_max->xMax = __MAX(p_max->xMax, p->xMax);
1148 p_max->yMax = __MAX(p_max->yMax, p->yMax);
1151 static int ProcessLines( filter_t *p_filter,
1152 line_desc_t **pp_lines,
1153 FT_BBox *p_bbox,
1154 int *pi_max_face_height,
1156 uni_char_t *psz_text,
1157 text_style_t **pp_styles,
1158 uint32_t *pi_k_dates,
1159 int i_len )
1161 filter_sys_t *p_sys = p_filter->p_sys;
1162 uni_char_t *p_fribidi_string = NULL;
1163 text_style_t **pp_fribidi_styles = NULL;
1164 int *p_new_positions = NULL;
1166 #if defined(HAVE_FRIBIDI)
1168 int *p_old_positions;
1169 int start_pos, pos = 0;
1171 pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1173 p_fribidi_string = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1174 p_old_positions = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1175 p_new_positions = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1177 if( ! pp_fribidi_styles ||
1178 ! p_fribidi_string ||
1179 ! p_old_positions ||
1180 ! p_new_positions )
1182 free( p_old_positions );
1183 free( p_new_positions );
1184 free( p_fribidi_string );
1185 free( pp_fribidi_styles );
1186 return VLC_ENOMEM;
1189 /* Do bidi conversion line-by-line */
1190 while(pos < i_len)
1192 while(pos < i_len) {
1193 if (psz_text[pos] != '\n')
1194 break;
1195 p_fribidi_string[pos] = psz_text[pos];
1196 pp_fribidi_styles[pos] = pp_styles[pos];
1197 p_new_positions[pos] = pos;
1198 ++pos;
1200 start_pos = pos;
1201 while(pos < i_len) {
1202 if (psz_text[pos] == '\n')
1203 break;
1204 ++pos;
1206 if (pos > start_pos)
1208 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1209 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1210 #else
1211 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1212 #endif
1213 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1214 pos - start_pos, &base_dir,
1215 (FriBidiChar*)p_fribidi_string + start_pos,
1216 p_new_positions + start_pos,
1217 p_old_positions,
1218 NULL );
1219 for( int j = start_pos; j < pos; j++ )
1221 pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
1222 p_new_positions[ j ] += start_pos;
1226 p_fribidi_string[ i_len ] = 0;
1227 free( p_old_positions );
1229 pp_styles = pp_fribidi_styles;
1230 psz_text = p_fribidi_string;
1232 #endif
1233 /* Work out the karaoke */
1234 uint8_t *pi_karaoke_bar = NULL;
1235 if( pi_k_dates )
1237 pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1238 if( pi_karaoke_bar )
1240 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1241 for( int i = 0; i < i_len; i++ )
1243 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
1244 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
1248 free( p_new_positions );
1250 *pi_max_face_height = 0;
1251 *pp_lines = NULL;
1252 line_desc_t **pp_line_next = pp_lines;
1254 FT_BBox bbox = {
1255 .xMin = INT_MAX,
1256 .yMin = INT_MAX,
1257 .xMax = INT_MIN,
1258 .yMax = INT_MIN,
1260 int i_face_height_previous = 0;
1261 int i_base_line = 0;
1262 const text_style_t *p_previous_style = NULL;
1263 FT_Face p_face = NULL;
1264 for( int i_start = 0; i_start < i_len; )
1266 /* Compute the length of the current text line */
1267 int i_length = 0;
1268 while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
1269 i_length++;
1271 /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
1272 line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
1273 int i_index = i_start;
1274 FT_Vector pen = {
1275 .x = 0,
1276 .y = 0,
1278 int i_face_height = 0;
1279 FT_BBox line_bbox = {
1280 .xMin = INT_MAX,
1281 .yMin = INT_MAX,
1282 .xMax = INT_MIN,
1283 .yMax = INT_MIN,
1285 int i_ul_offset = 0;
1286 int i_ul_thickness = 0;
1287 typedef struct {
1288 int i_index;
1289 FT_Vector pen;
1290 FT_BBox line_bbox;
1291 int i_face_height;
1292 int i_ul_offset;
1293 int i_ul_thickness;
1294 } break_point_t;
1295 break_point_t break_point;
1296 break_point_t break_point_fallback;
1298 #define SAVE_BP(dst) do { \
1299 dst.i_index = i_index; \
1300 dst.pen = pen; \
1301 dst.line_bbox = line_bbox; \
1302 dst.i_face_height = i_face_height; \
1303 dst.i_ul_offset = i_ul_offset; \
1304 dst.i_ul_thickness = i_ul_thickness; \
1305 } while(0)
1307 SAVE_BP( break_point );
1308 SAVE_BP( break_point_fallback );
1310 while( i_index < i_start + i_length )
1312 /* Split by common FT_Face + Size */
1313 const text_style_t *p_current_style = pp_styles[i_index];
1314 int i_part_length = 0;
1315 while( i_index + i_part_length < i_start + i_length )
1317 const text_style_t *p_style = pp_styles[i_index + i_part_length];
1318 if( !FaceStyleEquals( p_style, p_current_style ) ||
1319 p_style->i_font_size != p_current_style->i_font_size )
1320 break;
1321 i_part_length++;
1324 /* (Re)load/reconfigure the face if needed */
1325 if( !FaceStyleEquals( p_current_style, p_previous_style ) )
1327 if( p_face )
1328 FT_Done_Face( p_face );
1329 p_previous_style = NULL;
1331 p_face = LoadFace( p_filter, p_current_style );
1333 FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
1334 if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size ||
1335 ((p_previous_style->i_style_flags ^ p_current_style->i_style_flags) & STYLE_HALFWIDTH) )
1338 int i_font_width = ( p_current_style->i_style_flags & STYLE_HALFWIDTH )
1339 ? p_current_style->i_font_size / 2
1340 : p_current_style->i_font_size;
1341 if( FT_Set_Pixel_Sizes( p_current_face,
1342 i_font_width,
1343 p_current_style->i_font_size ) )
1344 msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
1345 if( p_sys->p_stroker )
1347 double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
1348 f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
1349 int i_radius = (p_current_style->i_font_size << 6) * f_outline_thickness;
1350 FT_Stroker_Set( p_sys->p_stroker,
1351 i_radius,
1352 FT_STROKER_LINECAP_ROUND,
1353 FT_STROKER_LINEJOIN_ROUND, 0 );
1356 p_previous_style = p_current_style;
1358 i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
1359 p_current_face->size->metrics.y_scale)));
1361 /* Render the part */
1362 bool b_break_line = false;
1363 int i_glyph_last = 0;
1364 while( i_part_length > 0 )
1366 const text_style_t *p_glyph_style = pp_styles[i_index];
1367 uni_char_t character = psz_text[i_index];
1368 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
1370 /* Get kerning vector */
1371 FT_Vector kerning = { .x = 0, .y = 0 };
1372 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
1374 FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
1376 if( p_glyph_style->i_spacing > 0 && i_glyph_last != 0 && i_glyph_index != 0 )
1378 kerning.x = (p_glyph_style->i_spacing) << 6;
1381 /* Get the glyph bitmap and its bounding box and all the associated properties */
1382 FT_Vector pen_new = {
1383 .x = pen.x + kerning.x,
1384 .y = pen.y + kerning.y,
1387 int i_font_width = ( p_current_style->i_style_flags & STYLE_HALFWIDTH )
1388 ? p_current_style->i_font_size / 2
1389 : p_current_style->i_font_size;
1390 FT_Vector pen_shadow_new = {
1391 .x = pen_new.x + p_sys->f_shadow_vector_x * (i_font_width << 6),
1392 .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
1395 FT_Glyph glyph;
1396 FT_BBox glyph_bbox;
1397 FT_Glyph outline;
1398 FT_BBox outline_bbox;
1399 FT_Glyph shadow;
1400 FT_BBox shadow_bbox;
1402 if( GetGlyph( p_filter,
1403 &glyph, &glyph_bbox,
1404 &outline, &outline_bbox,
1405 &shadow, &shadow_bbox,
1406 p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
1407 &pen_new, &pen_shadow_new ) )
1408 goto next;
1410 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
1411 if( outline )
1412 FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
1413 if( shadow )
1414 FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
1416 /* FIXME and what about outline */
1418 bool b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
1419 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
1420 (p_glyph_style->i_karaoke_background_alpha << 24))
1421 : (p_glyph_style->i_font_color |
1422 (p_glyph_style->i_font_alpha << 24));
1423 int i_line_offset = 0;
1424 int i_line_thickness = 0;
1425 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
1427 i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
1428 p_current_face->size->metrics.y_scale)) );
1430 i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
1431 p_current_face->size->metrics.y_scale)) );
1433 if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
1435 /* Move the baseline to make it strikethrough instead of
1436 * underline. That means that strikethrough takes precedence
1438 i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
1439 p_current_face->size->metrics.y_scale)) );
1441 else if( i_line_thickness > 0 )
1443 glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
1445 /* The real underline thickness and position are
1446 * updated once the whole line has been parsed */
1447 i_ul_offset = __MAX( i_ul_offset, i_line_offset );
1448 i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
1449 i_line_thickness = -1;
1452 FT_BBox line_bbox_new = line_bbox;
1453 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
1454 if( outline )
1455 BBoxEnlarge( &line_bbox_new, &outline_bbox );
1456 if( shadow )
1457 BBoxEnlarge( &line_bbox_new, &shadow_bbox );
1459 b_break_line = i_index > i_start &&
1460 line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
1461 if( b_break_line )
1463 FT_Done_Glyph( glyph );
1464 if( outline )
1465 FT_Done_Glyph( outline );
1466 if( shadow )
1467 FT_Done_Glyph( shadow );
1469 break_point_t *p_bp = NULL;
1470 if( break_point.i_index > i_start )
1471 p_bp = &break_point;
1472 else if( break_point_fallback.i_index > i_start )
1473 p_bp = &break_point_fallback;
1475 if( p_bp )
1477 msg_Dbg( p_filter, "Breaking line");
1478 for( int i = p_bp->i_index; i < i_index; i++ )
1480 line_character_t *ch = &p_line->p_character[i - i_start];
1481 FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1482 if( ch->p_outline )
1483 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1484 if( ch->p_shadow )
1485 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1487 p_line->i_character_count = p_bp->i_index - i_start;
1489 i_index = p_bp->i_index;
1490 pen = p_bp->pen;
1491 line_bbox = p_bp->line_bbox;
1492 i_face_height = p_bp->i_face_height;
1493 i_ul_offset = p_bp->i_ul_offset;
1494 i_ul_thickness = p_bp->i_ul_thickness;
1496 else
1498 msg_Err( p_filter, "Breaking unbreakable line");
1500 break;
1503 assert( p_line->i_character_count == i_index - i_start);
1504 p_line->p_character[p_line->i_character_count++] = (line_character_t){
1505 .p_glyph = (FT_BitmapGlyph)glyph,
1506 .p_outline = (FT_BitmapGlyph)outline,
1507 .p_shadow = (FT_BitmapGlyph)shadow,
1508 .i_color = i_color,
1509 .i_line_offset = i_line_offset,
1510 .i_line_thickness = i_line_thickness,
1513 pen.x = pen_new.x + p_current_face->glyph->advance.x;
1514 pen.y = pen_new.y + p_current_face->glyph->advance.y;
1515 line_bbox = line_bbox_new;
1516 next:
1517 i_glyph_last = i_glyph_index;
1518 i_part_length--;
1519 i_index++;
1521 if( character == ' ' || character == '\t' )
1522 SAVE_BP( break_point );
1523 else if( character == 160 )
1524 SAVE_BP( break_point_fallback );
1526 if( b_break_line )
1527 break;
1529 #undef SAVE_BP
1530 /* Update our baseline */
1531 if( i_face_height_previous > 0 )
1532 i_base_line += __MAX(i_face_height, i_face_height_previous);
1533 if( i_face_height > 0 )
1534 i_face_height_previous = i_face_height;
1536 /* Update the line bbox with the actual base line */
1537 if (line_bbox.yMax > line_bbox.yMin) {
1538 line_bbox.yMin -= i_base_line;
1539 line_bbox.yMax -= i_base_line;
1541 BBoxEnlarge( &bbox, &line_bbox );
1543 /* Terminate and append the line */
1544 if( p_line )
1546 p_line->i_width = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
1547 p_line->i_base_line = i_base_line;
1548 p_line->i_height = __MAX(i_face_height, i_face_height_previous);
1549 if( i_ul_thickness > 0 )
1551 for( int i = 0; i < p_line->i_character_count; i++ )
1553 line_character_t *ch = &p_line->p_character[i];
1554 if( ch->i_line_thickness < 0 )
1556 ch->i_line_offset = i_ul_offset;
1557 ch->i_line_thickness = i_ul_thickness;
1562 *pp_line_next = p_line;
1563 pp_line_next = &p_line->p_next;
1566 *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
1568 /* Skip what we have rendered and the line delimitor if present */
1569 i_start = i_index;
1570 if( i_start < i_len && psz_text[i_start] == '\n' )
1571 i_start++;
1573 if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
1575 msg_Err( p_filter, "Truncated too high subtitle" );
1576 break;
1579 if( p_face )
1580 FT_Done_Face( p_face );
1582 free( pp_fribidi_styles );
1583 free( p_fribidi_string );
1584 free( pi_karaoke_bar );
1586 *p_bbox = bbox;
1587 return VLC_SUCCESS;
1590 static xml_reader_t *GetXMLReader( filter_t *p_filter, stream_t *p_sub )
1592 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
1593 if( !p_xml_reader )
1594 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
1595 else
1596 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
1597 p_filter->p_sys->p_xml = p_xml_reader;
1599 return p_xml_reader;
1603 * This function renders a text subpicture region into another one.
1604 * It also calculates the size needed for this string, and renders the
1605 * needed glyphs into memory. It is used as pf_add_string callback in
1606 * the vout method by this module
1608 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
1609 subpicture_region_t *p_region_in, bool b_html,
1610 const vlc_fourcc_t *p_chroma_list )
1612 filter_sys_t *p_sys = p_filter->p_sys;
1614 if( !p_region_in )
1615 return VLC_EGENERIC;
1616 if( b_html && !p_region_in->psz_html )
1617 return VLC_EGENERIC;
1618 if( !b_html && !p_region_in->psz_text )
1619 return VLC_EGENERIC;
1621 const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
1622 : p_region_in->psz_text );
1624 uni_char_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
1625 text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
1626 if( !psz_text || !pp_styles )
1628 free( psz_text );
1629 free( pp_styles );
1630 return VLC_EGENERIC;
1633 /* Reset the default fontsize in case screen metrics have changed */
1634 p_filter->p_sys->style.i_font_size = GetFontSize( p_filter );
1636 /* */
1637 int rv = VLC_SUCCESS;
1638 int i_text_length = 0;
1639 FT_BBox bbox;
1640 int i_max_face_height;
1641 line_desc_t *p_lines = NULL;
1643 uint32_t *pi_k_durations = NULL;
1645 if( b_html )
1647 stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
1648 (uint8_t *) p_region_in->psz_html,
1649 strlen( p_region_in->psz_html ),
1650 true );
1651 if( unlikely(p_sub == NULL) )
1653 free( psz_text );
1654 free( pp_styles );
1655 return VLC_SUCCESS;
1658 xml_reader_t *p_xml_reader = GetXMLReader( p_filter, p_sub );
1660 if( !p_xml_reader )
1661 rv = VLC_EGENERIC;
1663 if( !rv )
1665 /* Look for Root Node */
1666 const char *node;
1668 if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
1670 if( strcasecmp( "karaoke", node ) == 0 )
1672 pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
1674 else if( strcasecmp( "text", node ) != 0 )
1676 /* Only text and karaoke tags are supported */
1677 msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
1678 node );
1679 rv = VLC_EGENERIC;
1682 else
1684 msg_Err( p_filter, "Malformed HTML subtitle" );
1685 rv = VLC_EGENERIC;
1688 if( !rv )
1690 rv = ProcessNodes( p_filter,
1691 psz_text, pp_styles, pi_k_durations, &i_text_length,
1692 p_xml_reader, p_region_in->p_style, &p_filter->p_sys->style );
1695 if( p_xml_reader )
1696 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
1698 stream_Delete( p_sub );
1700 else
1702 text_style_t *p_style;
1703 if( p_region_in->p_style )
1705 p_style = CreateStyle( p_region_in->p_style->psz_fontname ? p_region_in->p_style->psz_fontname
1706 : p_sys->style.psz_fontname,
1707 p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
1708 : p_sys->style.i_font_size,
1709 (p_region_in->p_style->i_font_color & 0xffffff) |
1710 ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
1711 0x00ffffff,
1712 p_region_in->p_style->i_style_flags & (STYLE_BOLD |
1713 STYLE_ITALIC |
1714 STYLE_UNDERLINE |
1715 STYLE_STRIKEOUT |
1716 STYLE_HALFWIDTH) );
1717 p_style->i_spacing = p_region_in->p_style->i_spacing;
1719 else
1721 uint32_t i_font_color = var_InheritInteger( p_filter, "freetype-color" );
1722 i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
1723 p_style = CreateStyle( p_sys->style.psz_fontname,
1724 p_sys->style.i_font_size,
1725 (i_font_color & 0xffffff) |
1726 ((p_sys->style.i_font_alpha & 0xff) << 24),
1727 0x00ffffff, 0);
1729 if( p_sys->style.i_style_flags & STYLE_BOLD )
1730 p_style->i_style_flags |= STYLE_BOLD;
1732 i_text_length = SetupText( p_filter,
1733 psz_text,
1734 pp_styles,
1735 NULL,
1736 p_region_in->psz_text, p_style, 0 );
1739 if( !rv && i_text_length > 0 )
1741 rv = ProcessLines( p_filter,
1742 &p_lines, &bbox, &i_max_face_height,
1743 psz_text, pp_styles, pi_k_durations, i_text_length );
1746 p_region_out->i_x = p_region_in->i_x;
1747 p_region_out->i_y = p_region_in->i_y;
1749 /* Don't attempt to render text that couldn't be layed out
1750 * properly. */
1751 if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
1753 const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
1754 const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
1756 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1757 p_chroma_list = p_chroma_list_yuvp;
1758 else if( !p_chroma_list || *p_chroma_list == 0 )
1759 p_chroma_list = p_chroma_list_rgba;
1761 uint8_t i_background_opacity = var_InheritInteger( p_filter, "freetype-background-opacity" );
1762 i_background_opacity = VLC_CLIP( i_background_opacity, 0, 255 );
1763 const int i_margin = i_background_opacity > 0 ? i_max_face_height / 4 : 0;
1764 for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
1766 rv = VLC_EGENERIC;
1767 if( *p_chroma == VLC_CODEC_YUVP )
1768 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
1769 else if( *p_chroma == VLC_CODEC_YUVA )
1770 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
1771 VLC_CODEC_YUVA,
1772 YUVFromRGB,
1773 FillYUVAPicture,
1774 BlendYUVAPixel );
1775 else if( *p_chroma == VLC_CODEC_RGBA )
1776 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
1777 VLC_CODEC_RGBA,
1778 RGBFromRGB,
1779 FillRGBAPicture,
1780 BlendRGBAPixel );
1781 else if( *p_chroma == VLC_CODEC_ARGB )
1782 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox,
1783 i_margin, *p_chroma, RGBFromRGB,
1784 FillARGBPicture, BlendARGBPixel );
1786 if( !rv )
1787 break;
1790 /* With karaoke, we're going to have to render the text a number
1791 * of times to show the progress marker on the text.
1793 if( pi_k_durations )
1794 var_SetBool( p_filter, "text-rerender", true );
1797 FreeLines( p_lines );
1799 free( psz_text );
1800 for( int i = 0; i < i_text_length; i++ )
1802 if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
1803 text_style_Delete( pp_styles[i] );
1805 free( pp_styles );
1806 free( pi_k_durations );
1808 return rv;
1811 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1812 subpicture_region_t *p_region_in,
1813 const vlc_fourcc_t *p_chroma_list )
1815 return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
1818 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
1819 subpicture_region_t *p_region_in,
1820 const vlc_fourcc_t *p_chroma_list )
1822 return RenderCommon( p_filter, p_region_out, p_region_in, true, p_chroma_list );
1825 /*****************************************************************************
1826 * Create: allocates osd-text video thread output method
1827 *****************************************************************************
1828 * This function allocates and initializes a Clone vout method.
1829 *****************************************************************************/
1830 static int Init_FT( vlc_object_t *p_this,
1831 const char *psz_fontfile,
1832 const int fontindex,
1833 const float f_outline_thickness)
1835 filter_t *p_filter = (filter_t *)p_this;
1836 filter_sys_t *p_sys = p_filter->p_sys;
1838 /* */
1839 int i_error = FT_Init_FreeType( &p_sys->p_library );
1840 if( i_error )
1842 msg_Err( p_filter, "couldn't initialize freetype" );
1843 goto error;
1846 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
1847 fontindex, &p_sys->p_face );
1849 if( i_error == FT_Err_Unknown_File_Format )
1851 msg_Err( p_filter, "file %s have unknown format",
1852 psz_fontfile ? psz_fontfile : "(null)" );
1853 goto error;
1855 else if( i_error )
1857 msg_Err( p_filter, "failed to load font file %s",
1858 psz_fontfile ? psz_fontfile : "(null)" );
1859 goto error;
1862 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
1863 if( i_error )
1865 msg_Err( p_filter, "font has no unicode translation table" );
1866 goto error;
1869 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
1871 p_sys->p_stroker = NULL;
1872 if( f_outline_thickness > .001f )
1874 i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
1875 if( i_error )
1876 msg_Err( p_filter, "Failed to create stroker for outlining" );
1879 return VLC_SUCCESS;
1881 error:
1882 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
1883 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
1885 return VLC_EGENERIC;
1889 static int Create( vlc_object_t *p_this )
1891 filter_t *p_filter = (filter_t *)p_this;
1892 filter_sys_t *p_sys;
1893 char *psz_fontfile = NULL;
1894 char *psz_fontname = NULL;
1895 char *psz_monofontfile = NULL;
1896 char *psz_monofontfamily = NULL;
1897 int fontindex = 0, monofontindex = 0;
1899 /* Allocate structure */
1900 p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
1901 if( !p_sys )
1902 return VLC_ENOMEM;
1904 p_sys->style.psz_fontname = NULL;
1905 p_sys->p_xml = NULL;
1906 p_sys->p_face = 0;
1907 p_sys->p_library = 0;
1908 p_sys->style.i_font_size = 0;
1909 p_sys->style.i_style_flags = 0;
1912 * The following variables should not be cached, as they might be changed on-the-fly:
1913 * freetype-rel-fontsize, freetype-background-opacity, freetype-background-color,
1914 * freetype-outline-thickness, freetype-color
1918 psz_fontname = var_InheritString( p_filter, "freetype-font" );
1919 psz_monofontfamily = var_InheritString( p_filter, "freetype-monofont" );
1920 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
1921 p_sys->style.i_font_alpha = var_InheritInteger( p_filter,"freetype-opacity" );
1922 p_sys->style.i_font_alpha = VLC_CLIP( p_sys->style.i_font_alpha, 0, 255 );
1923 if( var_InheritBool( p_filter, "freetype-bold" ) )
1924 p_sys->style.i_style_flags |= STYLE_BOLD;
1926 double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
1927 f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
1928 p_sys->style.i_outline_alpha = var_InheritInteger( p_filter, "freetype-outline-opacity" );
1929 p_sys->style.i_outline_alpha = VLC_CLIP( p_sys->style.i_outline_alpha, 0, 255 );
1930 p_sys->style.i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
1931 p_sys->style.i_outline_color = VLC_CLIP( p_sys->style.i_outline_color, 0, 0xFFFFFF );
1933 p_sys->style.i_shadow_alpha = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
1934 p_sys->style.i_shadow_alpha = VLC_CLIP( p_sys->style.i_shadow_alpha, 0, 255 );
1935 p_sys->style.i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
1936 p_sys->style.i_shadow_color = VLC_CLIP( p_sys->style.i_shadow_color, 0, 0xFFFFFF );
1937 float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
1938 float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
1939 f_shadow_distance = VLC_CLIP( f_shadow_distance, 0, 1 );
1940 p_sys->f_shadow_vector_x = f_shadow_distance * cosf((float)(2. * M_PI) * f_shadow_angle / 360);
1941 p_sys->f_shadow_vector_y = f_shadow_distance * sinf((float)(2. * M_PI) * f_shadow_angle / 360);
1943 /* Set default psz_fontname */
1944 if( !psz_fontname || !*psz_fontname )
1946 free( psz_fontname );
1947 #ifdef HAVE_GET_FONT_BY_FAMILY_NAME
1948 psz_fontname = strdup( DEFAULT_FAMILY );
1949 #else
1950 psz_fontname = File_Select( DEFAULT_FONT_FILE );
1951 #endif
1954 /* set default psz_monofontname */
1955 if( !psz_monofontfamily || !*psz_monofontfamily )
1957 free( psz_monofontfamily );
1958 #ifdef HAVE_GET_FONT_BY_FAMILY_NAME
1959 psz_monofontfamily = strdup( DEFAULT_MONOSPACE_FAMILY );
1960 #else
1961 psz_monofontfamily = File_Select( DEFAULT_MONOSPACE_FONT_FILE );
1962 #endif
1965 /* Set the current font file */
1966 p_sys->style.psz_fontname = psz_fontname;
1967 p_sys->style.psz_monofontname = psz_monofontfamily;
1969 #ifdef HAVE_FONTCONFIG
1970 p_sys->pf_select = FontConfig_Select;
1971 FontConfig_BuildCache( p_filter );
1972 #elif defined( __APPLE__ )
1973 #if !TARGET_OS_IPHONE
1974 p_sys->pf_select = MacLegacy_Select;
1975 #endif
1976 #elif defined( _WIN32 ) && defined( HAVE_GET_FONT_BY_FAMILY_NAME )
1977 p_sys->pf_select = Win32_Select;
1978 #else
1979 p_sys->pf_select = Dummy_Select;
1980 #endif
1982 /* */
1983 psz_fontfile = p_sys->pf_select( p_filter, psz_fontname, false, false,
1984 p_sys->i_default_font_size, &fontindex );
1985 psz_monofontfile = p_sys->pf_select( p_filter, psz_monofontfamily, false,
1986 false, p_sys->i_default_font_size,
1987 &monofontindex );
1988 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontname, psz_fontfile );
1989 msg_Dbg( p_filter, "Using %s as mono-font from file %s", psz_monofontfamily, psz_monofontfile );
1991 /* If nothing is found, use the default family */
1992 if( !psz_fontfile )
1993 psz_fontfile = File_Select( psz_fontname );
1994 if( !psz_monofontfile )
1995 psz_monofontfile = File_Select( psz_monofontfamily );
1997 if( Init_FT( p_this, psz_fontfile, fontindex, f_outline_thickness ) != VLC_SUCCESS )
1998 goto error;
2000 p_sys->pp_font_attachments = NULL;
2001 p_sys->i_font_attachments = 0;
2003 p_filter->pf_render_text = RenderText;
2004 p_filter->pf_render_html = RenderHtml;
2006 LoadFontsFromAttachments( p_filter );
2008 free( psz_fontfile );
2009 free( psz_monofontfile );
2011 return VLC_SUCCESS;
2013 error:
2014 free( psz_fontfile );
2015 free( psz_monofontfile );
2016 free( psz_fontname );
2017 free( psz_monofontfamily );
2018 free( p_sys );
2019 return VLC_EGENERIC;
2023 static void Destroy_FT( vlc_object_t *p_this )
2025 filter_t *p_filter = (filter_t *)p_this;
2026 filter_sys_t *p_sys = p_filter->p_sys;
2028 if( p_sys->p_stroker )
2029 FT_Stroker_Done( p_sys->p_stroker );
2030 FT_Done_Face( p_sys->p_face );
2031 FT_Done_FreeType( p_sys->p_library );
2034 /*****************************************************************************
2035 * Destroy: destroy Clone video thread output method
2036 *****************************************************************************
2037 * Clean up all data and library connections
2038 *****************************************************************************/
2039 static void Destroy( vlc_object_t *p_this )
2041 filter_t *p_filter = (filter_t *)p_this;
2042 filter_sys_t *p_sys = p_filter->p_sys;
2044 if( p_sys->pp_font_attachments )
2046 for( int k = 0; k < p_sys->i_font_attachments; k++ )
2047 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2049 free( p_sys->pp_font_attachments );
2052 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2053 free( p_sys->style.psz_fontname );
2054 free( p_sys->style.psz_monofontname );
2056 Destroy_FT( p_this );
2057 free( p_sys );