#include <vlc_dialog.h> /* FcCache dialog */
#include <vlc_filter.h> /* filter_sys_t */
#include <vlc_text_style.h> /* text_style_t*/
-#include <vlc_memory.h> /* realloc_or_free */
/* Default fonts */
#ifdef __APPLE__
"fonts that will be rendered on the video. If absolute font size is set, "\
"relative size will be overridden." )
+#define BG_OPACITY_TEXT N_("Background opacity")
+#define BG_COLOR_TEXT N_("Background color")
+
static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
static const char *const ppsz_sizes_text[] = {
N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
#define YUVP_TEXT N_("Use YUVP renderer")
#define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
"This option is only needed if you want to encode into DVB subtitles" )
-#define EFFECT_TEXT N_("Font Effect")
-#define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
-"text to improve its readability." )
-
-enum { EFFECT_BACKGROUND = 1,
- EFFECT_OUTLINE = 2,
- EFFECT_OUTLINE_FAT = 3,
-};
-static int const pi_effects[] = { EFFECT_BACKGROUND, EFFECT_OUTLINE, EFFECT_OUTLINE_FAT };
-static const char *const ppsz_effects_text[] = {
- N_("Background"),N_("Outline"), N_("Fat Outline") };
static const int pi_color_values[] = {
0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
change_integer_list( pi_color_values, ppsz_color_descriptions )
change_safe()
+ add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
+ BG_OPACITY_TEXT, "", false )
+ change_safe()
+ add_integer( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
+ "", false )
+ change_integer_list( pi_color_values, ppsz_color_descriptions )
+ change_safe()
+
add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
FONTSIZER_LONGTEXT, false )
change_integer_list( pi_sizes, ppsz_sizes_text )
change_safe()
- add_integer( "freetype-effect", 2, EFFECT_TEXT,
- EFFECT_LONGTEXT, false )
- change_integer_list( pi_effects, ppsz_effects_text )
- change_safe()
+ add_obsolete_integer( "freetype-effect" );
add_bool( "freetype-yuvp", false, YUVP_TEXT,
YUVP_LONGTEXT, true )
* Local prototypes
*****************************************************************************/
+typedef struct
+{
+ FT_BitmapGlyph p_glyph;
+ uint32_t i_color; /* ARGB color */
+ int i_line_offset; /* underline/strikethrough offset */
+ int i_line_thickness; /* underline/strikethrough thickness */
+} line_character_t;
+
typedef struct line_desc_t line_desc_t;
struct line_desc_t
{
- /** NULL-terminated list of glyphs making the string */
- FT_BitmapGlyph *pp_glyphs;
- /** list of relative positions for the glyphs */
- FT_Vector *p_glyph_pos;
- /** list of RGB information for styled text
- * -- if the rendering mode supports it (RenderYUVA) and
- * b_new_color_mode is set, then it becomes possible to
- * have multicoloured text within the subtitles. */
- uint32_t *p_fg_rgb;
- uint32_t *p_bg_rgb;
- uint8_t *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
- bool b_new_color_mode;
- /** underline information -- only supplied if text should be underlined */
- int *pi_underline_offset;
- uint16_t *pi_underline_thickness;
-
- int i_height;
- int i_width;
- int i_red, i_green, i_blue;
- int i_alpha;
-
line_desc_t *p_next;
+
+ int i_width;
+ int i_base_line;
+ int i_character_count;
+ line_character_t *p_character;
};
-static line_desc_t *NewLine( int );
-static void FreeLines( line_desc_t * );
typedef struct font_stack_t font_stack_t;
struct font_stack_t
{
FT_Library p_library; /* handle to library */
FT_Face p_face; /* handle to face object */
- bool i_use_kerning;
uint8_t i_font_opacity;
int i_font_color;
int i_font_size;
- int i_effect;
+
+ uint8_t i_background_opacity;
+ int i_background_color;
int i_default_font_size;
int i_display_height;
return VLC_EGENERIC;
p_sys->i_font_attachments = 0;
- p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
+ p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
if( !p_sys->pp_font_attachments )
return VLC_ENOMEM;
* This function merges the previously rendered freetype glyphs into a picture
*****************************************************************************/
static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
- line_desc_t *p_line, int i_width, int i_height )
+ line_desc_t *p_line,
+ FT_BBox *p_bbox )
{
VLC_UNUSED(p_filter);
static const uint8_t pi_gamma[16] =
uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
/* Create a new subpicture region */
- memset( &fmt, 0, sizeof(video_format_t) );
- fmt.i_chroma = VLC_CODEC_YUVP;
- fmt.i_width = fmt.i_visible_width = i_width + 4;
- fmt.i_height = fmt.i_visible_height = i_height + 4;
- if( p_region->fmt.i_visible_width > 0 )
- fmt.i_visible_width = p_region->fmt.i_visible_width;
- if( p_region->fmt.i_visible_height > 0 )
- fmt.i_visible_height = p_region->fmt.i_visible_height;
- fmt.i_x_offset = fmt.i_y_offset = 0;
- fmt.i_sar_num = 1;
- fmt.i_sar_den = 1;
+ video_format_Init( &fmt, VLC_CODEC_YUVP );
+ fmt.i_width =
+ fmt.i_visible_width = p_bbox->xMax - p_bbox->xMin + 4;
+ fmt.i_height =
+ fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
assert( !p_region->p_picture );
p_region->p_picture = picture_NewFromFormat( &fmt );
fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
p_region->fmt = fmt;
- /* Calculate text color components */
- YUVFromRGB( (p_line->i_red << 16) |
- (p_line->i_green << 8) |
- (p_line->i_blue ),
- &i_y, &i_u, &i_v);
+ /* Calculate text color components
+ * Only use the first color */
+ int i_alpha = 0xff - ((p_line->p_character[0].i_color >> 24) & 0xff);
+ YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
/* Build palette */
fmt.p_palette->i_entries = 16;
fmt.p_palette->palette[i][2] = 0x80;
fmt.p_palette->palette[i][3] = pi_gamma[i];
fmt.p_palette->palette[i][3] =
- (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
+ (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
}
for( i = 8; i < fmt.p_palette->i_entries; i++ )
{
fmt.p_palette->palette[i][2] = i_v;
fmt.p_palette->palette[i][3] = pi_gamma[i];
fmt.p_palette->palette[i][3] =
- (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
+ (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
}
p_dst = p_region->p_picture->Y_PIXELS;
for( ; p_line != NULL; p_line = p_line->p_next )
{
- int i_glyph_tmax = 0;
- int i_bitmap_offset, i_offset, i_align_offset = 0;
- for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
- {
- FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
- i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
- }
-
- if( p_line->i_width < i_width )
+ int i_align_left = 0;
+ if( p_line->i_width < fmt.i_visible_width )
{
if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
- {
- i_align_offset = i_width - p_line->i_width;
- }
+ i_align_left = ( fmt.i_visible_width - p_line->i_width );
else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
- {
- i_align_offset = ( i_width - p_line->i_width ) / 2;
- }
+ i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
}
+ int i_align_top = 0;
- for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
+ for( i = 0; i < p_line->i_character_count; i++ )
{
- FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
+ const line_character_t *ch = &p_line->p_character[i];
+ FT_BitmapGlyph p_glyph = ch->p_glyph;
- i_offset = ( p_line->p_glyph_pos[ i ].y +
- i_glyph_tmax - p_glyph->top + 2 ) *
- i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
- i_align_offset;
+ int i_glyph_y = i_align_top - p_glyph->top + p_bbox->yMax + p_line->i_base_line;
+ int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
- for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
+ for( y = 0; y < p_glyph->bitmap.rows; y++ )
{
- for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
+ for( x = 0; x < p_glyph->bitmap.width; x++ )
{
- if( p_glyph->bitmap.buffer[i_bitmap_offset] )
- p_dst[i_offset+x] =
- ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
+ if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
+ p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
+ (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
}
- i_offset += i_pitch;
}
}
}
return VLC_SUCCESS;
}
-static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
- FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
- FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
- int i_glyph_tmax, int i_align_offset,
- uint8_t i_y, uint8_t i_u, uint8_t i_v,
- subpicture_region_t *p_region)
+/*****************************************************************************
+ * RenderYUVA: place string in picture
+ *****************************************************************************
+ * This function merges the previously rendered freetype glyphs into a picture
+ *****************************************************************************/
+static inline void BlendYUVAPixel( picture_t *p_picture,
+ int i_picture_x, int i_picture_y,
+ int i_a, int i_y, int i_u, int i_v,
+ int i_alpha )
{
- int i_pitch;
- uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
-
- p_dst_y = p_region->p_picture->Y_PIXELS;
- p_dst_u = p_region->p_picture->U_PIXELS;
- p_dst_v = p_region->p_picture->V_PIXELS;
- p_dst_a = p_region->p_picture->A_PIXELS;
- i_pitch = p_region->p_picture->A_PITCH;
+ int i_an = i_a * i_alpha / 255;
- int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
- p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
+ uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
+ uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
+ uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
+ uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
- for( int y = 0; y < i_line_thickness; y++ )
+ int i_ao = *p_a;
+ if( i_ao == 0 )
{
- int i_extra = p_this_glyph->bitmap.width;
-
- if( b_ul_next_char )
- {
- i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
- (p_this_glyph_pos->x + p_this_glyph->left);
- }
- for( int x = 0; x < i_extra; x++ )
+ *p_y = i_y;
+ *p_u = i_u;
+ *p_v = i_v;
+ *p_a = i_an;
+ }
+ else
+ {
+ *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
+ if( *p_a != 0 )
{
- bool b_ok = true;
-
- /* break the underline around the tails of any glyphs which cross it */
- /* Strikethrough doesn't get broken */
- for( int z = x - i_line_thickness;
- z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
- z++ )
- {
- if( p_next_glyph && ( z >= i_extra ) )
- {
- int i_row = i_line_offset + p_next_glyph->top + y;
-
- if( ( p_next_glyph->bitmap.rows > i_row ) &&
- p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
- {
- b_ok = false;
- }
- }
- else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
- {
- int i_row = i_line_offset + p_this_glyph->top + y;
-
- if( ( p_this_glyph->bitmap.rows > i_row ) &&
- p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
- {
- b_ok = false;
- }
- }
- }
-
- if( b_ok )
- {
- p_dst_y[i_offset+x] = (i_y * 255) >> 8;
- p_dst_u[i_offset+x] = i_u;
- p_dst_v[i_offset+x] = i_v;
- p_dst_a[i_offset+x] = 255;
- }
+ *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
+ *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
+ *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
}
- i_offset += i_pitch;
}
}
-static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
+static inline void BlendYUVAGlyph( picture_t *p_picture,
+ int i_picture_x, int i_picture_y,
+ int i_a, int i_y, int i_u, int i_v,
+ FT_BitmapGlyph p_glyph )
{
- uint8_t *p_dst = p_region->p_picture->A_PIXELS;
- int i_pitch = p_region->p_picture->A_PITCH;
- int y;
-
- for( ; p_line != NULL; p_line = p_line->p_next )
+ for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
{
- int i_glyph_tmax=0, i = 0;
- int i_bitmap_offset, i_offset, i_align_offset = 0;
- for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
- {
- FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
- i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
- }
-
- if( p_line->i_width < i_width )
- {
- if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
- {
- i_align_offset = i_width - p_line->i_width;
- }
- else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
- {
- i_align_offset = ( i_width - p_line->i_width ) / 2;
- }
- }
-
- for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
- {
- FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
+ for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
+ BlendYUVAPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
+ i_a, i_y, i_u, i_v,
+ p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
+ }
+}
- i_offset = ( p_line->p_glyph_pos[ i ].y +
- i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
- i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
- i_align_offset +xoffset;
+static inline void BlendYUVALine( picture_t *p_picture,
+ int i_picture_x, int i_picture_y,
+ int i_a, int i_y, int i_u, int i_v,
+ const line_character_t *p_current,
+ const line_character_t *p_next )
+{
+ int i_line_width = p_current->p_glyph->bitmap.width;
+ if( p_next )
+ i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
- for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
- {
- for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
- {
- if( p_glyph->bitmap.buffer[i_bitmap_offset] )
- if( p_dst[i_offset+x] <
- ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
- p_dst[i_offset+x] =
- ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
- }
- i_offset += i_pitch;
- }
- }
+ for( int dx = 0; dx < i_line_width; dx++ )
+ {
+ for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
+ BlendYUVAPixel( p_picture,
+ i_picture_x + dx,
+ i_picture_y + p_current->i_line_offset + dy,
+ i_a, i_y, i_u, i_v, 0xff );
}
}
-/*****************************************************************************
- * RenderYUVA: place string in picture
- *****************************************************************************
- * This function merges the previously rendered freetype glyphs into a picture
- *****************************************************************************/
-static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
- line_desc_t *p_line, int i_width, int i_height )
+static int RenderYUVA( filter_t *p_filter,
+ subpicture_region_t *p_region,
+ line_desc_t *p_line_head,
+ FT_BBox *p_bbox,
+ int i_margin )
{
- uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
- video_format_t fmt;
- int i, y, i_pitch, i_alpha;
- uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
-
- if( i_width == 0 || i_height == 0 )
- return VLC_SUCCESS;
+ filter_sys_t *p_sys = p_filter->p_sys;
/* Create a new subpicture region */
- memset( &fmt, 0, sizeof(video_format_t) );
- fmt.i_chroma = VLC_CODEC_YUVA;
- fmt.i_width = fmt.i_visible_width = i_width + 6;
- fmt.i_height = fmt.i_visible_height = i_height + 6;
- if( p_region->fmt.i_visible_width > 0 )
- fmt.i_visible_width = p_region->fmt.i_visible_width;
- if( p_region->fmt.i_visible_height > 0 )
- fmt.i_visible_height = p_region->fmt.i_visible_height;
- fmt.i_x_offset = fmt.i_y_offset = 0;
- fmt.i_sar_num = 1;
- fmt.i_sar_den = 1;
+ const int i_text_width = p_bbox->xMax - p_bbox->xMin;
+ const int i_text_height = p_bbox->yMax - p_bbox->yMin;
+ video_format_t fmt;
+ video_format_Init( &fmt, VLC_CODEC_YUVA );
+ fmt.i_width =
+ fmt.i_visible_width = i_text_width + 2 * i_margin;
+ fmt.i_height =
+ fmt.i_visible_height = i_text_height + 2 * i_margin;
- p_region->p_picture = picture_NewFromFormat( &fmt );
+ picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
if( !p_region->p_picture )
return VLC_EGENERIC;
p_region->fmt = fmt;
- /* Calculate text color components */
- YUVFromRGB( (p_line->i_red << 16) |
- (p_line->i_green << 8) |
- (p_line->i_blue ),
- &i_y, &i_u, &i_v);
- i_alpha = p_line->i_alpha;
-
- p_dst_y = p_region->p_picture->Y_PIXELS;
- p_dst_u = p_region->p_picture->U_PIXELS;
- p_dst_v = p_region->p_picture->V_PIXELS;
- p_dst_a = p_region->p_picture->A_PIXELS;
- i_pitch = p_region->p_picture->A_PITCH;
-
- /* Initialize the region pixels */
- if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
- {
- memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
- memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
- memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
- memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
- }
- else
- {
- memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
- memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
- memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
- memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
- }
- if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
- p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
- {
- DrawBlack( p_line, i_width, p_region, 0, 0);
- DrawBlack( p_line, i_width, p_region, -1, 0);
- DrawBlack( p_line, i_width, p_region, 0, -1);
- DrawBlack( p_line, i_width, p_region, 1, 0);
- DrawBlack( p_line, i_width, p_region, 0, 1);
- }
-
- if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
- {
- DrawBlack( p_line, i_width, p_region, -1, -1);
- DrawBlack( p_line, i_width, p_region, -1, 1);
- DrawBlack( p_line, i_width, p_region, 1, -1);
- DrawBlack( p_line, i_width, p_region, 1, 1);
-
- DrawBlack( p_line, i_width, p_region, -2, 0);
- DrawBlack( p_line, i_width, p_region, 0, -2);
- DrawBlack( p_line, i_width, p_region, 2, 0);
- DrawBlack( p_line, i_width, p_region, 0, 2);
-
- DrawBlack( p_line, i_width, p_region, -2, -2);
- DrawBlack( p_line, i_width, p_region, -2, 2);
- DrawBlack( p_line, i_width, p_region, 2, -2);
- DrawBlack( p_line, i_width, p_region, 2, 2);
-
- DrawBlack( p_line, i_width, p_region, -3, 0);
- DrawBlack( p_line, i_width, p_region, 0, -3);
- DrawBlack( p_line, i_width, p_region, 3, 0);
- DrawBlack( p_line, i_width, p_region, 0, 3);
- }
-
- for( ; p_line != NULL; p_line = p_line->p_next )
+ /* Initialize the picture background */
+ uint8_t i_a = p_sys->i_background_opacity;
+ uint8_t i_y, i_u, i_v;
+ YUVFromRGB( p_sys->i_background_color, &i_y, &i_u, &i_v );
+
+ memset( p_picture->p[0].p_pixels, i_y,
+ p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
+ memset( p_picture->p[1].p_pixels, i_u,
+ p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
+ memset( p_picture->p[2].p_pixels, i_v,
+ p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
+ memset( p_picture->p[3].p_pixels, i_a,
+ p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
+
+ /* Render all lines */
+ for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
{
- int i_glyph_tmax = 0;
- int i_bitmap_offset, i_offset, i_align_offset = 0;
- for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
- {
- FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
- i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
- }
-
- if( p_line->i_width < i_width )
+ int i_align_left = i_margin;
+ if( p_line->i_width < i_text_width )
{
+ /* Left offset to take into account alignment */
if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
- {
- i_align_offset = i_width - p_line->i_width;
- }
+ i_align_left += ( i_text_width - p_line->i_width );
else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
- {
- i_align_offset = ( i_width - p_line->i_width ) / 2;
- }
+ i_align_left += ( i_text_width - p_line->i_width ) / 2;
}
+ int i_align_top = i_margin;
- for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
+ /* Render all glyphs and underline/strikethrough */
+ for( int i = 0; i < p_line->i_character_count; i++ )
{
- FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
-
- i_offset = ( p_line->p_glyph_pos[ i ].y +
- i_glyph_tmax - p_glyph->top + 3 ) *
- i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
- i_align_offset;
-
- if( p_line->b_new_color_mode )
- {
- /* Every glyph can (and in fact must) have its own color */
- YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
- }
-
- for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
- {
- for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
- {
- uint8_t i_y_local = i_y;
- uint8_t i_u_local = i_u;
- uint8_t i_v_local = i_v;
-
- if( p_line->p_fg_bg_ratio != 0x00 )
- {
- int i_split = p_glyph->bitmap.width *
- p_line->p_fg_bg_ratio[ i ] / 0x7f;
-
- if( x > i_split )
- {
- YUVFromRGB( p_line->p_bg_rgb[ i ],
- &i_y_local, &i_u_local, &i_v_local );
- }
- }
-
- if( p_glyph->bitmap.buffer[i_bitmap_offset] )
- {
- p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
- i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
-
- p_dst_u[i_offset+x] = i_u;
- p_dst_v[i_offset+x] = i_v;
-
- if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
- p_dst_a[i_offset+x] = 0xff;
- }
- }
- i_offset += i_pitch;
- }
-
- if( p_line->pi_underline_thickness[ i ] )
- {
- UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
- p_line->pi_underline_offset[ i ],
- (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
- p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
- p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
- i_glyph_tmax, i_align_offset,
- i_y, i_u, i_v,
- p_region);
- }
+ const line_character_t *ch = &p_line->p_character[i];
+ FT_BitmapGlyph p_glyph = ch->p_glyph;
+
+ i_a = 0xff - ((ch->i_color >> 24) & 0xff);
+ YUVFromRGB( ch->i_color, &i_y, &i_u, &i_v );
+
+ int i_glyph_y = i_align_top - p_glyph->top + p_bbox->yMax + p_line->i_base_line;
+ int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
+
+ BlendYUVAGlyph( p_picture,
+ i_glyph_x, i_glyph_y,
+ i_a, i_y, i_u, i_v,
+ p_glyph );
+
+ if( ch->i_line_thickness > 0 )
+ BlendYUVALine( p_picture,
+ i_glyph_x, i_glyph_y + p_glyph->top,
+ i_a, i_y, i_u, i_v,
+ &ch[0],
+ i + 1 < p_line->i_character_count ? &ch[1] : NULL );
}
}
- /* Apply the alpha setting */
- for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
- p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
-
return VLC_SUCCESS;
}
static text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
uint32_t i_font_color, uint32_t i_karaoke_bg_color,
- bool b_bold, bool b_italic, bool b_uline, bool b_through )
+ int i_style_flags )
{
text_style_t *p_style = text_style_New();
if( !p_style )
return NULL;
- p_style->psz_fontname = strdup( psz_fontname );
+ p_style->psz_fontname = psz_fontname ? strdup( psz_fontname ) : NULL;
p_style->i_font_size = i_font_size;
p_style->i_font_color = (i_font_color & 0x00ffffff) >> 0;
p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >> 0;
p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
- if( b_bold )
- p_style->i_style_flags |= STYLE_BOLD;
- if( b_italic )
- p_style->i_style_flags |= STYLE_ITALIC;
- if( b_uline )
- p_style->i_style_flags |= STYLE_UNDERLINE;
- if( b_through )
- p_style->i_style_flags |= STYLE_STRIKEOUT;
-
+ p_style->i_style_flags |= i_style_flags;
return p_style;
}
-static bool StyleEquals( text_style_t *s1, text_style_t *s2 )
-{
- if( !s1 || !s2 )
- return false;
- if( s1 == s2 )
- return true;
-
- return s1->i_font_size == s2->i_font_size &&
- s1->i_font_color == s2->i_font_color &&
- s1->i_font_alpha == s2->i_font_alpha &&
- s1->i_style_flags == s2->i_style_flags &&
- !strcmp( s1->psz_fontname, s2->psz_fontname );
-}
-
-static void IconvText( filter_t *p_filter, const char *psz_string,
- size_t *i_string_length, uint32_t *psz_unicode )
-{
- *i_string_length = 0;
- if( psz_unicode == NULL )
- return;
-
- size_t i_length;
- uint32_t *psz_tmp =
-#if defined(WORDS_BIGENDIAN)
- ToCharset( "UCS-4BE", psz_string, &i_length );
-#else
- ToCharset( "UCS-4LE", psz_string, &i_length );
-#endif
- if( !psz_tmp )
- {
- msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
- return;
- }
- memcpy( psz_unicode, psz_tmp, i_length );
- *i_string_length = i_length / 4;
-
- free( psz_tmp );
-}
-
static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
uint32_t i_color, uint32_t i_karaoke_bg_color )
{
- font_stack_t *p_new;
-
if( !p_font )
return VLC_EGENERIC;
- p_new = malloc( sizeof( font_stack_t ) );
- if( ! p_new )
+ font_stack_t *p_new = malloc( sizeof(*p_new) );
+ if( !p_new )
return VLC_ENOMEM;
p_new->p_next = NULL;
return rv;
}
-static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
- uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
-{
- /* Karaoke tags _PRECEDE_ the text they specify a duration
- * for, therefore we are working out the length for the
- * previous tag, and first time through we have nothing
- */
- if( pi_k_run_lengths )
- {
- int i_chars = 0;
- uint32_t i;
-
- /* Work out how many characters are presently in the string
- */
- for( i = 0; i < i_runs; i++ )
- i_chars += pi_run_lengths[ i ];
-
- /* Subtract away those we've already allocated to other
- * karaoke tags
- */
- for( i = 0; i < i_k_runs; i++ )
- i_chars -= pi_k_run_lengths[ i ];
-
- pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
- }
-}
-
-static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
- uint32_t **ppi_k_run_lengths,
- uint32_t **ppi_k_durations )
-{
- const char *name, *value;
-
- while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
- {
- if( !strcasecmp( "t", name ) )
- {
- if( ppi_k_durations && ppi_k_run_lengths )
- {
- (*pi_k_runs)++;
-
- if( *ppi_k_durations )
- {
- *ppi_k_durations = realloc_or_free( *ppi_k_durations,
- *pi_k_runs * sizeof( uint32_t ) );
- }
- else if( *pi_k_runs == 1 )
- {
- *ppi_k_durations = (uint32_t *)
- malloc( *pi_k_runs * sizeof( uint32_t ) );
- }
-
- if( *ppi_k_run_lengths )
- {
- *ppi_k_run_lengths = realloc_or_free( *ppi_k_run_lengths,
- *pi_k_runs * sizeof( uint32_t ) );
- }
- else if( *pi_k_runs == 1 )
- {
- *ppi_k_run_lengths = (uint32_t *)
- malloc( *pi_k_runs * sizeof( uint32_t ) );
- }
- if( *ppi_k_durations )
- (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( value );
-
- if( *ppi_k_run_lengths )
- (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
- }
- }
- }
-}
-
/* Turn any multiple-whitespaces into single spaces */
static void HandleWhiteSpace( char *psz_node )
{
static text_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
font_stack_t **p_fonts,
- bool b_bold, bool b_italic,
- bool b_uline, bool b_through )
+ int i_style_flags )
{
char *psz_fontname = NULL;
uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
return NULL;
return CreateStyle( psz_fontname, i_font_size, i_font_color,
- i_karaoke_bg_color, b_bold, b_italic, b_uline, b_through );
+ i_karaoke_bg_color,
+ i_style_flags );
}
-static int RenderTag( filter_t *p_filter, FT_Face p_face,
- int i_font_color,
- int i_style_flags,
- int i_karaoke_bgcolor,
- line_desc_t *p_line, uint32_t *psz_unicode,
- int *pi_pen_x, int i_pen_y, int *pi_start,
- FT_Vector *p_result )
-{
- FT_BBox line;
- int i_yMin, i_yMax;
- int i;
- bool b_first_on_line = true;
-
- int i_previous = 0;
- int i_pen_x_start = *pi_pen_x;
-
- uint32_t *psz_unicode_start = psz_unicode;
+static unsigned SetupText( filter_t *p_filter,
+ uint32_t *psz_text_out,
+ text_style_t **pp_styles,
+ uint32_t *pi_k_dates,
- line.xMin = line.xMax = line.yMin = line.yMax = 0;
+ const char *psz_text_in,
+ text_style_t *p_style,
+ uint32_t i_k_date )
+{
+ size_t i_string_length;
- /* Account for part of line already in position */
- for( i = 0; i<*pi_start; i++ )
+ size_t i_string_bytes;
+#if defined(WORDS_BIGENDIAN)
+ uint32_t *psz_tmp = ToCharset( "UCS-4BE", psz_text_in, &i_string_bytes );
+#else
+ uint32_t *psz_tmp = ToCharset( "UCS-4LE", psz_text_in, &i_string_bytes );
+#endif
+ if( psz_tmp )
{
- FT_BBox glyph_size;
-
- FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
- ft_glyph_bbox_pixels, &glyph_size );
-
- line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
- glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
- line.yMax = __MAX( line.yMax, glyph_size.yMax );
- line.yMin = __MIN( line.yMin, glyph_size.yMin );
+ memcpy( psz_text_out, psz_tmp, i_string_bytes );
+ i_string_length = i_string_bytes / 4;
+ free( psz_tmp );
}
- i_yMin = line.yMin;
- i_yMax = line.yMax;
-
- if( line.xMax > 0 )
- b_first_on_line = false;
-
- while( *psz_unicode && ( *psz_unicode != '\n' ) )
+ else
{
- FT_BBox glyph_size;
- FT_Glyph tmp_glyph;
- int i_error;
-
- int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
- if( FT_HAS_KERNING( p_face ) && i_glyph_index
- && i_previous )
- {
- FT_Vector delta;
- FT_Get_Kerning( p_face, i_previous, i_glyph_index,
- ft_kerning_default, &delta );
- *pi_pen_x += delta.x >> 6;
- }
- p_line->p_glyph_pos[ i ].x = *pi_pen_x;
- p_line->p_glyph_pos[ i ].y = i_pen_y;
-
- i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT );
- if( i_error )
- {
- i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
- if( i_error )
- {
- msg_Err( p_filter,
- "unable to render text FT_Load_Glyph returned %d", i_error );
- p_line->pp_glyphs[ i ] = NULL;
- return VLC_EGENERIC;
- }
- }
-
- /* Do synthetic styling now that Freetype supports it;
- * ie. if the font we have loaded is NOT already in the
- * style that the tags want, then switch it on; if they
- * are then don't. */
- if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
- FT_GlyphSlot_Embolden( p_face->glyph );
- if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
- FT_GlyphSlot_Oblique( p_face->glyph );
-
- i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
- if( i_error )
- {
- msg_Err( p_filter,
- "unable to render text FT_Get_Glyph returned %d", i_error );
- p_line->pp_glyphs[ i ] = NULL;
- return VLC_EGENERIC;
- }
- FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
- i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
- if( i_error )
- {
- FT_Done_Glyph( tmp_glyph );
- continue;
- }
- if( i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
- {
- float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
- p_face->size->metrics.y_scale));
- float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
- p_face->size->metrics.y_scale));
-
- p_line->pi_underline_offset[ i ] =
- ( aOffset < 0 ) ? -aOffset : aOffset;
- p_line->pi_underline_thickness[ i ] =
- ( aSize < 0 ) ? -aSize : aSize;
- if (i_style_flags & STYLE_STRIKEOUT)
- {
- /* Move the baseline to make it strikethrough instead of
- * underline. That means that strikethrough takes precedence
- */
- float aDescent = FT_FLOOR(FT_MulFix(p_face->descender*2,
- p_face->size->metrics.y_scale));
-
- p_line->pi_underline_offset[ i ] -=
- ( aDescent < 0 ) ? -aDescent : aDescent;
- }
- }
-
- p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
- p_line->p_fg_rgb[ i ] = i_font_color;
- p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor;
- p_line->p_fg_bg_ratio[ i ] = 0x00;
-
- line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
- glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
- if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
- {
- for( ; i >= *pi_start; i-- )
- FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
- i = *pi_start;
-
- while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
- {
- psz_unicode--;
- }
- if( psz_unicode == psz_unicode_start )
- {
- if( b_first_on_line )
- {
- msg_Warn( p_filter, "unbreakable string" );
- p_line->pp_glyphs[ i ] = NULL;
- return VLC_EGENERIC;
- }
- *pi_pen_x = i_pen_x_start;
-
- p_line->i_width = line.xMax;
- p_line->i_height = __MAX( p_line->i_height,
- p_face->size->metrics.height >> 6 );
- p_line->pp_glyphs[ i ] = NULL;
-
- p_result->x = __MAX( p_result->x, line.xMax );
- p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
- i_yMax - i_yMin ) );
- return VLC_SUCCESS;
- }
- else
- {
- *psz_unicode = '\n';
- }
- psz_unicode = psz_unicode_start;
- *pi_pen_x = i_pen_x_start;
- i_previous = 0;
-
- line.yMax = i_yMax;
- line.yMin = i_yMin;
-
- continue;
- }
- line.yMax = __MAX( line.yMax, glyph_size.yMax );
- line.yMin = __MIN( line.yMin, glyph_size.yMin );
-
- i_previous = i_glyph_index;
- *pi_pen_x += p_face->glyph->advance.x >> 6;
- i++;
+ msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
+ i_string_length = 0;
}
- p_line->i_width = line.xMax;
- p_line->i_height = __MAX( p_line->i_height,
- p_face->size->metrics.height >> 6 );
- p_line->pp_glyphs[ i ] = NULL;
-
- p_result->x = __MAX( p_result->x, line.xMax );
- p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
- line.yMax - line.yMin ) );
- *pi_start = i;
-
- /* Get rid of any text processed - if necessary repositioning
- * at the start of a new line of text
- */
- if( !*psz_unicode )
+ if( i_string_length > 0 )
+ {
+ for( unsigned i = 0; i < i_string_length; i++ )
+ pp_styles[i] = p_style;
+ }
+ else
{
- *psz_unicode_start = '\0';
+ text_style_Delete( p_style );
}
- else if( psz_unicode > psz_unicode_start )
+ if( i_string_length > 0 && pi_k_dates )
{
- for( i=0; psz_unicode[ i ]; i++ )
- psz_unicode_start[ i ] = psz_unicode[ i ];
- psz_unicode_start[ i ] = '\0';
+ for( unsigned i = 0; i < i_string_length; i++ )
+ pi_k_dates[i] = i_k_date;
}
-
- return VLC_SUCCESS;
+ return i_string_length;
}
-static void SetupLine( filter_t *p_filter, const char *psz_text_in,
- uint32_t **ppsz_text_out, uint32_t *pi_runs,
- uint32_t **ppi_run_lengths, text_style_t ***ppp_styles,
- text_style_t *p_style )
+static int ProcessNodes( filter_t *p_filter,
+ uint32_t *psz_text,
+ text_style_t **pp_styles,
+ uint32_t *pi_k_dates,
+ int *pi_len,
+ xml_reader_t *p_xml_reader,
+ text_style_t *p_font_style )
{
- size_t i_string_length;
+ int rv = VLC_SUCCESS;
+ filter_sys_t *p_sys = p_filter->p_sys;
+ int i_text_length = 0;
+ font_stack_t *p_fonts = NULL;
+ uint32_t i_k_date = 0;
- IconvText( p_filter, psz_text_in, &i_string_length, *ppsz_text_out );
- *ppsz_text_out += i_string_length;
+ int i_style_flags = 0;
- if( ppp_styles && ppi_run_lengths )
+ if( p_font_style )
{
- (*pi_runs)++;
-
- /* XXX this logic looks somewhat broken */
+ rv = PushFont( &p_fonts,
+ p_font_style->psz_fontname,
+ p_font_style->i_font_size,
+ (p_font_style->i_font_color & 0xffffff) |
+ ((p_font_style->i_font_alpha & 0xff) << 24),
+ (p_font_style->i_karaoke_background_color & 0xffffff) |
+ ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
- if( *ppp_styles )
- {
- *ppp_styles = realloc_or_free( *ppp_styles,
- *pi_runs * sizeof( text_style_t * ) );
- }
- else if( *pi_runs == 1 )
- {
- *ppp_styles = malloc( *pi_runs * sizeof( text_style_t * ) );
- }
-
- /* We have just malloc'ed this memory successfully -
- * *pi_runs HAS to be within the memory area of *ppp_styles */
- if( *ppp_styles )
- {
- (*ppp_styles)[ *pi_runs - 1 ] = p_style;
- p_style = NULL;
- }
-
- /* XXX more iffy logic */
-
- if( *ppi_run_lengths )
- {
- *ppi_run_lengths = realloc_or_free( *ppi_run_lengths,
- *pi_runs * sizeof( uint32_t ) );
- }
- else if( *pi_runs == 1 )
- {
- *ppi_run_lengths = (uint32_t *)
- malloc( *pi_runs * sizeof( uint32_t ) );
- }
-
- /* same remarks here */
- if( *ppi_run_lengths )
- {
- (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
- }
- }
- /* If we couldn't use the p_style argument due to memory allocation
- * problems above, release it here.
- */
- text_style_Delete( p_style );
-}
-
-static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, text_style_t *p_style )
-{
- for( int k = 0; k < p_sys->i_font_attachments; k++ )
- {
- input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
- int i_font_idx = 0;
- FT_Face p_face = NULL;
-
- while( 0 == FT_New_Memory_Face( p_sys->p_library,
- p_attach->p_data,
- p_attach->i_data,
- i_font_idx,
- &p_face ))
- {
- if( p_face )
- {
- bool match = !strcasecmp( p_face->family_name,
- p_style->psz_fontname );
-
- if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
- match &= (p_style->i_style_flags & STYLE_BOLD) != 0;
- else
- match &= (p_style->i_style_flags & STYLE_BOLD) == 0;
-
- if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
- match &= (p_style->i_style_flags & STYLE_ITALIC) != 0;
- else
- match &= (p_style->i_style_flags & STYLE_ITALIC) == 0;
-
- if( match )
- {
- *pp_face = p_face;
- return VLC_SUCCESS;
- }
-
- FT_Done_Face( p_face );
- }
- i_font_idx++;
- }
- }
- return VLC_EGENERIC;
-}
-
-static int ProcessNodes( filter_t *p_filter,
- xml_reader_t *p_xml_reader,
- text_style_t *p_font_style,
- uint32_t *psz_text,
- int *pi_len,
-
- uint32_t *pi_runs,
- uint32_t **ppi_run_lengths,
- text_style_t * **ppp_styles,
-
- bool b_karaoke,
- uint32_t *pi_k_runs,
- uint32_t **ppi_k_run_lengths,
- uint32_t **ppi_k_durations )
-{
- int rv = VLC_SUCCESS;
- filter_sys_t *p_sys = p_filter->p_sys;
- uint32_t *psz_text_orig = psz_text;
- font_stack_t *p_fonts = NULL;
-
- bool b_italic = false;
- bool b_bold = false;
- bool b_uline = false;
- bool b_through = false;
-
- if( p_font_style )
- {
- rv = PushFont( &p_fonts,
- p_font_style->psz_fontname,
- p_font_style->i_font_size,
- (p_font_style->i_font_color & 0xffffff) |
- ((p_font_style->i_font_alpha & 0xff) << 24),
- (p_font_style->i_karaoke_background_color & 0xffffff) |
- ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
-
- if( p_font_style->i_style_flags & STYLE_BOLD )
- b_bold = true;
- if( p_font_style->i_style_flags & STYLE_ITALIC )
- b_italic = true;
- if( p_font_style->i_style_flags & STYLE_UNDERLINE )
- b_uline = true;
- if( p_font_style->i_style_flags & STYLE_STRIKEOUT )
- b_through = true;
+ i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
+ STYLE_ITALIC |
+ STYLE_UNDERLINE |
+ STYLE_STRIKEOUT);
}
#ifdef HAVE_STYLES
else
if( !strcasecmp( "font", node ) )
PopFont( &p_fonts );
else if( !strcasecmp( "b", node ) )
- b_bold = false;
+ i_style_flags &= ~STYLE_BOLD;
else if( !strcasecmp( "i", node ) )
- b_italic = false;
+ i_style_flags &= ~STYLE_ITALIC;
else if( !strcasecmp( "u", node ) )
- b_uline = false;
+ i_style_flags &= ~STYLE_UNDERLINE;
else if( !strcasecmp( "s", node ) )
- b_through = false;
+ i_style_flags &= ~STYLE_STRIKEOUT;
break;
case XML_READER_STARTELEM:
if( !strcasecmp( "font", node ) )
- rv = HandleFontAttributes( p_xml_reader, &p_fonts );
+ HandleFontAttributes( p_xml_reader, &p_fonts );
else if( !strcasecmp( "b", node ) )
- b_bold = true;
+ i_style_flags |= STYLE_BOLD;
else if( !strcasecmp( "i", node ) )
- b_italic = true;
+ i_style_flags |= STYLE_ITALIC;
else if( !strcasecmp( "u", node ) )
- b_uline = true;
+ i_style_flags |= STYLE_UNDERLINE;
else if( !strcasecmp( "s", node ) )
- b_through = true;
+ i_style_flags |= STYLE_STRIKEOUT;
else if( !strcasecmp( "br", node ) )
{
- SetupLine( p_filter, "\n", &psz_text,
- pi_runs, ppi_run_lengths, ppp_styles,
- GetStyleFromFontStack( p_sys,
- &p_fonts,
- b_bold,
- b_italic,
- b_uline,
- b_through) );
+ i_text_length += SetupText( p_filter,
+ &psz_text[i_text_length],
+ &pp_styles[i_text_length],
+ pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
+ "\n",
+ GetStyleFromFontStack( p_sys,
+ &p_fonts,
+ i_style_flags ),
+ i_k_date );
}
else if( !strcasecmp( "k", node ) )
{
- /* Only valid in karaoke */
- if( b_karaoke )
+ /* Karaoke tags */
+ const char *name, *value;
+ while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
{
- if( *pi_k_runs > 0 )
- SetKaraokeLen( *pi_runs, *ppi_run_lengths,
- *pi_k_runs, *ppi_k_run_lengths );
- SetupKaraoke( p_xml_reader, pi_k_runs,
- ppi_k_run_lengths, ppi_k_durations );
+ if( !strcasecmp( "t", name ) && value )
+ i_k_date += atoi( value );
}
}
break;
HandleWhiteSpace( psz_node );
resolve_xml_special_chars( psz_node );
- SetupLine( p_filter, psz_node, &psz_text,
- pi_runs, ppi_run_lengths, ppp_styles,
- GetStyleFromFontStack( p_sys,
- &p_fonts,
- b_bold,
- b_italic,
- b_uline,
- b_through) );
+ i_text_length += SetupText( p_filter,
+ &psz_text[i_text_length],
+ &pp_styles[i_text_length],
+ pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
+ psz_node,
+ GetStyleFromFontStack( p_sys,
+ &p_fonts,
+ i_style_flags ),
+ i_k_date );
free( psz_node );
break;
}
}
- if( rv != VLC_SUCCESS )
- {
- psz_text = psz_text_orig;
- break;
- }
- }
- if( b_karaoke )
- {
- SetKaraokeLen( *pi_runs, *ppi_run_lengths,
- *pi_k_runs, *ppi_k_run_lengths );
}
- *pi_len = psz_text - psz_text_orig;
+ *pi_len = i_text_length;
while( VLC_SUCCESS == PopFont( &p_fonts ) );
- return rv;
+ return VLC_SUCCESS;
}
+static void FreeLine( line_desc_t *p_line )
+{
+ for( int i = 0; i < p_line->i_character_count; i++ )
+ FT_Done_Glyph( (FT_Glyph)p_line->p_character[i].p_glyph );
-static int ProcessLines( filter_t *p_filter,
- uint32_t *psz_text,
- int i_len,
+ free( p_line->p_character );
+ free( p_line );
+}
- uint32_t i_runs,
- uint32_t *pi_run_lengths,
- text_style_t **pp_styles,
- line_desc_t **pp_lines,
+static void FreeLines( line_desc_t *p_lines )
+{
+ for( line_desc_t *p_line = p_lines; p_line != NULL; )
+ {
+ line_desc_t *p_next = p_line->p_next;
+ FreeLine( p_line );
+ p_line = p_next;
+ }
+}
+
+static line_desc_t *NewLine( int i_count )
+{
+ line_desc_t *p_line = malloc( sizeof(*p_line) );
- FT_Vector *p_result,
+ if( !p_line )
+ return NULL;
+
+ p_line->p_next = NULL;
+ p_line->i_width = 0;
+ p_line->i_base_line = 0;
+ p_line->i_character_count = 0;
+
+ p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
+ if( !p_line->p_character )
+ {
+ free( p_line );
+ return NULL;
+ }
+ return p_line;
+}
- bool b_karaoke,
- uint32_t i_k_runs,
- uint32_t *pi_k_run_lengths,
- uint32_t *pi_k_durations )
+static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
{
- filter_sys_t *p_sys = p_filter->p_sys;
- text_style_t **pp_char_styles;
- int *p_new_positions = NULL;
- int8_t *p_levels = NULL;
- uint8_t *pi_karaoke_bar = NULL;
- uint32_t i, j, k;
- int i_prev;
-
- /* Assign each character in the text string its style explicitly, so that
- * after the characters have been shuffled around by Fribidi, we can re-apply
- * the styles, and to simplify the calculation of runs within a line.
- */
- pp_char_styles = (text_style_t **) malloc( i_len * sizeof( text_style_t * ));
- if( !pp_char_styles )
- return VLC_ENOMEM;
+ for( int k = 0; k < p_sys->i_font_attachments; k++ )
+ {
+ input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
+ int i_font_idx = 0;
+ FT_Face p_face = NULL;
+
+ while( 0 == FT_New_Memory_Face( p_sys->p_library,
+ p_attach->p_data,
+ p_attach->i_data,
+ i_font_idx,
+ &p_face ))
+ {
+ if( p_face )
+ {
+ int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD) ? STYLE_BOLD : 0) |
+ ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
+ if( !strcasecmp( p_face->family_name, p_style->psz_fontname ) &&
+ (p_style->i_style_flags & (STYLE_BOLD | STYLE_BOLD)) == i_style_received )
+ return p_face;
+
+ FT_Done_Face( p_face );
+ }
+ i_font_idx++;
+ }
+ }
+ return NULL;
+}
+
+static FT_Face LoadFace( filter_t *p_filter,
+ const text_style_t *p_style )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ /* Look for a match amongst our attachments first */
+ FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
+
+ /* Load system wide font otheriwse */
+ if( !p_face )
+ {
+ int i_idx = 0;
+ char *psz_fontfile;
+#ifdef HAVE_FONTCONFIG
+ psz_fontfile = FontConfig_Select( NULL,
+ p_style->psz_fontname,
+ (p_style->i_style_flags & STYLE_BOLD) != 0,
+ (p_style->i_style_flags & STYLE_ITALIC) != 0,
+ -1,
+ &i_idx );
+#elif defined( WIN32 )
+ psz_fontfile = Win32_Select( p_filter,
+ p_style->psz_fontname,
+ (p_style->i_style_flags & STYLE_BOLD) != 0,
+ (p_style->i_style_flags & STYLE_ITALIC) != 0,
+ -1,
+ &i_idx );
+#else
+ psz_fontfile = NULL;
+#endif
+ if( !psz_fontfile )
+ return NULL;
+
+ if( *psz_fontfile == '\0' )
+ {
+ msg_Warn( p_filter,
+ "We were not able to find a matching font: \"%s\" (%s %s),"
+ " so using default font",
+ p_style->psz_fontname,
+ (p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
+ (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
+ p_face = NULL;
+ }
+ else
+ {
+ if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
+ p_face = NULL;
+ }
+ free( psz_fontfile );
+ }
+ if( !p_face )
+ return NULL;
- if( b_karaoke )
+ if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
{
- pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
- /* If we can't allocate sufficient memory for karaoke, continue anyway -
- * we just won't be able to display the progress bar; at least we'll
- * get the text.
+ /* We've loaded a font face which is unhelpful for actually
+ * rendering text - fallback to the default one.
*/
+ FT_Done_Face( p_face );
+ return NULL;
+ }
+ return p_face;
+}
+
+static bool FaceStyleEquals( const text_style_t *p_style1,
+ const text_style_t *p_style2 )
+{
+ if( !p_style1 || !p_style2 )
+ return false;
+ if( p_style1 == p_style2 )
+ return true;
+
+ const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
+ return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
+ !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
+}
+
+static int GetGlyph( filter_t *p_filter,
+ FT_Glyph *pp_glyph,
+ FT_BBox *p_bbox,
+
+ FT_Face p_face,
+ int i_glyph_index,
+ int i_style_flags,
+ FT_Vector *p_pen )
+{
+ if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
+ FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
+ {
+ msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
+ return VLC_EGENERIC;
+ }
+
+ /* Do synthetic styling now that Freetype supports it;
+ * ie. if the font we have loaded is NOT already in the
+ * style that the tags want, then switch it on; if they
+ * are then don't. */
+ if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
+ FT_GlyphSlot_Embolden( p_face->glyph );
+ if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
+ FT_GlyphSlot_Oblique( p_face->glyph );
+
+ FT_Glyph glyph;
+ if( FT_Get_Glyph( p_face->glyph, &glyph ) )
+ {
+ msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
+ return VLC_EGENERIC;
+ }
+
+ if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
+ {
+ FT_Done_Glyph( glyph );
+ return VLC_EGENERIC;
}
- i = 0;
- for( j = 0; j < i_runs; j++ )
- for( k = 0; k < pi_run_lengths[ j ]; k++ )
- pp_char_styles[ i++ ] = pp_styles[ j ];
+ FT_BBox bbox;
+ FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, &bbox );
+
+ *pp_glyph = glyph;
+ *p_bbox = bbox;
+ return VLC_SUCCESS;
+}
+
+static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
+{
+ p_max->xMin = __MIN(p_max->xMin, p->xMin);
+ p_max->yMin = __MIN(p_max->yMin, p->yMin);
+ p_max->xMax = __MAX(p_max->xMax, p->xMax);
+ p_max->yMax = __MAX(p_max->yMax, p->yMax);
+}
+
+static int ProcessLines( filter_t *p_filter,
+ line_desc_t **pp_lines,
+ FT_BBox *p_bbox,
+ int *pi_max_face_height,
+
+ uint32_t *psz_text,
+ text_style_t **pp_styles,
+ uint32_t *pi_k_dates,
+ int i_len )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ uint32_t *p_fribidi_string = NULL;
+ text_style_t **pp_fribidi_styles = NULL;
+ int *p_new_positions = NULL;
#if defined(HAVE_FRIBIDI)
{
- text_style_t **pp_char_styles_new;
- int *p_old_positions;
- uint32_t *p_fribidi_string;
+ int *p_old_positions;
int start_pos, pos = 0;
- pp_char_styles_new = (text_style_t **)malloc( i_len * sizeof( text_style_t * ));
+ pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
- p_fribidi_string = (uint32_t *)
- malloc( (i_len + 1) * sizeof(uint32_t) );
- p_old_positions = (int *)
- malloc( (i_len + 1) * sizeof( int ) );
- p_new_positions = (int *)
- malloc( (i_len + 1) * sizeof( int ) );
- p_levels = (int8_t *)
- malloc( (i_len + 1) * sizeof( int8_t ) );
+ p_fribidi_string = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
+ p_old_positions = malloc( (i_len + 1) * sizeof(*p_old_positions) );
+ p_new_positions = malloc( (i_len + 1) * sizeof(*p_new_positions) );
- if( ! pp_char_styles_new ||
+ if( ! pp_fribidi_styles ||
! p_fribidi_string ||
! p_old_positions ||
- ! p_new_positions ||
- ! p_levels )
+ ! p_new_positions )
{
- free( p_levels );
free( p_old_positions );
free( p_new_positions );
free( p_fribidi_string );
- free( pp_char_styles_new );
- free( pi_karaoke_bar );
-
- free( pp_char_styles );
+ free( pp_fribidi_styles );
return VLC_ENOMEM;
}
if (psz_text[pos] != '\n')
break;
p_fribidi_string[pos] = psz_text[pos];
- pp_char_styles_new[pos] = pp_char_styles[pos];
+ pp_fribidi_styles[pos] = pp_styles[pos];
p_new_positions[pos] = pos;
- p_levels[pos] = 0;
++pos;
}
start_pos = pos;
(FriBidiChar*)p_fribidi_string + start_pos,
p_new_positions + start_pos,
p_old_positions,
- p_levels + start_pos );
- for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
+ NULL );
+ for( int j = start_pos; j < pos; j++ )
{
- pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
- p_old_positions[ j - start_pos ] ];
+ pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
p_new_positions[ j ] += start_pos;
}
}
}
+ p_fribidi_string[ i_len ] = 0;
free( p_old_positions );
- free( pp_char_styles );
- pp_char_styles = pp_char_styles_new;
+
+ pp_styles = pp_fribidi_styles;
psz_text = p_fribidi_string;
- p_fribidi_string[ i_len ] = 0;
}
#endif
/* Work out the karaoke */
- if( pi_karaoke_bar )
+ uint8_t *pi_karaoke_bar = NULL;
+ if( pi_k_dates )
{
- int64_t i_last_duration = 0;
- int64_t i_duration = 0;
- int64_t i_start_pos = 0;
- int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
-
- for( k = 0; k< i_k_runs; k++ )
+ pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
+ if( pi_karaoke_bar )
{
- double fraction = 0.0;
-
- i_duration += pi_k_durations[ k ];
-
- if( i_duration < i_elapsed )
- {
- /* Completely finished this run-length -
- * let it render normally */
-
- fraction = 1.0;
- }
- else if( i_elapsed < i_last_duration )
- {
- /* Haven't got up to this segment yet -
- * render it completely in karaoke BG mode */
-
- fraction = 0.0;
- }
- else
- {
- /* Partway through this run */
-
- fraction = (double)(i_elapsed - i_last_duration) /
- (double)pi_k_durations[ k ];
- }
- for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
- {
- double shade = pi_k_run_lengths[ k ] * fraction;
-
- if( p_new_positions )
- j = p_new_positions[ i_start_pos + i ];
- else
- j = i_start_pos + i;
-
- if( i < (uint32_t)shade )
- pi_karaoke_bar[ j ] = 0xff;
- else if( (double)i > shade )
- pi_karaoke_bar[ j ] = 0x00;
- else
- {
- shade -= (int)shade;
- pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
- ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
- }
- }
-
- i_last_duration = i_duration;
- i_start_pos += pi_k_run_lengths[ k ];
+ int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
+ for( int i = 0; i < i_len; i++ )
+ {
+ unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
+ pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
+ }
}
}
- free( p_levels );
free( p_new_positions );
- FT_Vector tmp_result;
-
- line_desc_t *p_line = NULL;
- line_desc_t *p_prev = NULL;
-
- int i_pen_x = 0;
- int i_pen_y = 0;
- int i_posn = 0;
-
- p_result->x = p_result->y = 0;
- tmp_result.x = tmp_result.y = 0;
-
- i_prev = 0;
- for( k = 0; k <= (uint32_t) i_len; k++ )
+ *pi_max_face_height = 0;
+ *pp_lines = NULL;
+ line_desc_t **pp_line_next = pp_lines;
+
+ FT_BBox bbox = {
+ .xMin = INT_MAX,
+ .yMin = INT_MAX,
+ .xMax = INT_MIN,
+ .yMax = INT_MIN,
+ };
+ int i_face_height_previous = 0;
+ int i_base_line = 0;
+ const text_style_t *p_previous_style = NULL;
+ FT_Face p_face = NULL;
+ for( int i_start = 0; i_start < i_len; )
{
- if( ( k == (uint32_t) i_len ) ||
- ( ( k > 0 ) &&
- !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
+ /* Compute the length of the current text line */
+ int i_length = 0;
+ while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
+ i_length++;
+
+ /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
+ line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
+ int i_index = i_start;
+ FT_Vector pen = {
+ .x = 0,
+ .y = 0,
+ };
+ int i_face_height = 0;
+ FT_BBox line_bbox = {
+ .xMin = INT_MAX,
+ .yMin = INT_MAX,
+ .xMax = INT_MIN,
+ .yMax = INT_MIN,
+ };
+ int i_ul_offset = 0;
+ int i_ul_thickness = 0;
+ typedef struct {
+ int i_index;
+ FT_Vector pen;
+ FT_BBox line_bbox;
+ int i_face_height;
+ int i_ul_offset;
+ int i_ul_thickness;
+ } break_point_t;
+ break_point_t break_point;
+ break_point_t break_point_fallback;
+
+#define SAVE_BP(dst) do { \
+ dst.i_index = i_index; \
+ dst.pen = pen; \
+ dst.line_bbox = line_bbox; \
+ dst.i_face_height = i_face_height; \
+ dst.i_ul_offset = i_ul_offset; \
+ dst.i_ul_thickness = i_ul_thickness; \
+ } while(0)
+
+ SAVE_BP( break_point );
+ SAVE_BP( break_point_fallback );
+
+ while( i_index < i_start + i_length )
{
- text_style_t *p_style = pp_char_styles[ k - 1 ];
-
- /* End of the current style run */
- FT_Face p_face = NULL;
- int i_idx = 0;
+ /* Split by common FT_Face + Size */
+ const text_style_t *p_current_style = pp_styles[i_index];
+ int i_part_length = 0;
+ while( i_index + i_part_length < i_start + i_length )
+ {
+ const text_style_t *p_style = pp_styles[i_index + i_part_length];
+ if( !FaceStyleEquals( p_style, p_current_style ) ||
+ p_style->i_font_size != p_current_style->i_font_size )
+ break;
+ i_part_length++;
+ }
- /* Look for a match amongst our attachments first */
- CheckForEmbeddedFont( p_sys, &p_face, p_style );
+ /* (Re)load/reconfigure the face if needed */
+ if( !FaceStyleEquals( p_current_style, p_previous_style ) )
+ {
+ if( p_face )
+ FT_Done_Face( p_face );
+ p_previous_style = NULL;
- if( ! p_face )
+ p_face = LoadFace( p_filter, p_current_style );
+ }
+ FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
+ if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
{
- char *psz_fontfile;
+ if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
+ msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
+ }
+ p_previous_style = p_current_style;
-#ifdef HAVE_FONTCONFIG
- psz_fontfile = FontConfig_Select( NULL,
- p_style->psz_fontname,
- (p_style->i_style_flags & STYLE_BOLD) != 0,
- (p_style->i_style_flags & STYLE_ITALIC) != 0,
- -1,
- &i_idx );
-#elif defined( WIN32 )
- psz_fontfile = Win32_Select( p_filter,
- p_style->psz_fontname,
- p_style->b_bold,
- p_style->b_italic,
- -1,
- &i_idx );
-#else
- psz_fontfile = NULL;
-#endif
- if( psz_fontfile && ! *psz_fontfile )
+ i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
+ p_current_face->size->metrics.y_scale)));
+
+ /* Render the part */
+ bool b_break_line = false;
+ int i_glyph_last = 0;
+ while( i_part_length > 0 )
+ {
+ const text_style_t *p_glyph_style = pp_styles[i_index];
+ uint32_t character = psz_text[i_index];
+ int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
+
+ /* Get kerning vector */
+ FT_Vector kerning = { .x = 0, .y = 0 };
+ if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
+ FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
+
+ /* Get the glyph bitmap and its bounding box and all the associated properties */
+ FT_Vector pen_new = {
+ .x = pen.x + kerning.x,
+ .y = pen.y + kerning.y,
+ };
+ FT_Glyph glyph;
+ FT_BBox glyph_bbox;
+ if( GetGlyph( p_filter, &glyph, &glyph_bbox,
+ p_current_face, i_glyph_index, p_glyph_style->i_style_flags, &pen_new ) )
+ goto next;
+
+ FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
+ if( glyph_bbox.xMin >= glyph_bbox.xMax )
{
- msg_Warn( p_filter,
- "We were not able to find a matching font: \"%s\" (%s %s),"
- " so using default font",
- p_style->psz_fontname,
- (p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
- (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
- free( psz_fontfile );
- psz_fontfile = NULL;
+ glyph_bbox.xMin = FT_CEIL(pen_new.x);
+ glyph_bbox.xMax = FT_CEIL(pen_new.x + p_current_face->glyph->advance.x);
+ glyph_bmp->left = glyph_bbox.xMin;
}
-
- if( psz_fontfile )
+ if( glyph_bbox.yMin >= glyph_bbox.yMax )
{
- if( FT_New_Face( p_sys->p_library,
- psz_fontfile, i_idx, &p_face ) )
- {
- free( psz_fontfile );
- free( pp_char_styles );
-#if defined(HAVE_FRIBIDI)
- free( psz_text );
-#endif
- free( pi_karaoke_bar );
- return VLC_EGENERIC;
- }
- free( psz_fontfile );
+ glyph_bbox.yMax = FT_CEIL(pen_new.y);
+ glyph_bbox.yMin = FT_CEIL(pen_new.y + p_current_face->glyph->advance.y);
+ glyph_bmp->top = glyph_bbox.yMax;
}
- }
- if( p_face &&
- FT_Select_Charmap( p_face, ft_encoding_unicode ) )
- {
- /* We've loaded a font face which is unhelpful for actually
- * rendering text - fallback to the default one.
- */
- FT_Done_Face( p_face );
- p_face = NULL;
- }
- if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
- ft_encoding_unicode ) ||
- FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
- p_style->i_font_size ) )
- {
- if( p_face ) FT_Done_Face( p_face );
- free( pp_char_styles );
-#if defined(HAVE_FRIBIDI)
- free( psz_text );
-#endif
- free( pi_karaoke_bar );
- return VLC_EGENERIC;
- }
- p_sys->i_use_kerning =
- FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
+ bool b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
+ uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
+ (p_glyph_style->i_karaoke_background_alpha << 24))
+ : (p_glyph_style->i_font_color |
+ (p_glyph_style->i_font_alpha << 24));
+ int i_line_offset = 0;
+ int i_line_thickness = 0;
+ if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
+ {
+ i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
+ p_current_face->size->metrics.y_scale)) );
+ i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
+ p_current_face->size->metrics.y_scale)) );
- uint32_t *psz_unicode = (uint32_t *)
- malloc( (k - i_prev + 1) * sizeof( uint32_t ));
- if( !psz_unicode )
- {
- if( p_face ) FT_Done_Face( p_face );
- free( pp_char_styles );
- free( psz_unicode );
-#if defined(HAVE_FRIBIDI)
- free( psz_text );
-#endif
- free( pi_karaoke_bar );
- return VLC_ENOMEM;
- }
- memcpy( psz_unicode, psz_text + i_prev,
- sizeof( uint32_t ) * ( k - i_prev ) );
- psz_unicode[ k - i_prev ] = 0;
- while( *psz_unicode )
- {
- if( !p_line )
- {
- if( !(p_line = NewLine( i_len - i_prev)) )
+ if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
{
- if( p_face ) FT_Done_Face( p_face );
- free( pp_char_styles );
- free( psz_unicode );
-#if defined(HAVE_FRIBIDI)
- free( psz_text );
-#endif
- free( pi_karaoke_bar );
- return VLC_ENOMEM;
+ /* Move the baseline to make it strikethrough instead of
+ * underline. That means that strikethrough takes precedence
+ */
+ i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
+ p_current_face->size->metrics.y_scale)) );
}
- /* New Color mode only works in YUVA rendering mode --
- * (RGB mode has palette constraints on it). We therefore
- * need to populate the legacy colour fields also.
- */
- p_line->b_new_color_mode = true;
- p_line->i_alpha = ( p_style->i_font_alpha & 0x000000ff ) >> 0;
- p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
- p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
- p_line->i_blue = ( p_style->i_font_color & 0x000000ff ) >> 0;
- p_line->p_next = NULL;
- i_pen_x = 0;
- i_pen_y += tmp_result.y;
- tmp_result.x = 0;
- tmp_result.y = 0;
- i_posn = 0;
- if( p_prev ) p_prev->p_next = p_line;
- else *pp_lines = p_line;
- }
+ else if( i_line_thickness > 0 )
+ {
+ glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
- if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
- p_style->i_font_color,
- p_style->i_style_flags,
- p_style->i_karaoke_background_color,
- p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
- &tmp_result ) != VLC_SUCCESS )
- {
- if( p_face ) FT_Done_Face( p_face );
- free( pp_char_styles );
- free( psz_unicode );
-#if defined(HAVE_FRIBIDI)
- free( psz_text );
-#endif
- free( pi_karaoke_bar );
- return VLC_EGENERIC;
+ /* The real underline thickness and position are
+ * updated once the whole line has been parsed */
+ i_ul_offset = __MAX( i_ul_offset, i_line_offset );
+ i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
+ i_line_thickness = -1;
+ }
}
+ FT_BBox line_bbox_new = line_bbox;
+ BBoxEnlarge( &line_bbox_new, &glyph_bbox );
- if( *psz_unicode )
+ b_break_line = i_index > i_start &&
+ line_bbox_new.xMax - line_bbox_new.xMin >= p_filter->fmt_out.video.i_visible_width;
+ if( b_break_line )
{
- p_result->x = __MAX( p_result->x, tmp_result.x );
- p_result->y += tmp_result.y;
+ FT_Done_Glyph( glyph );
- p_prev = p_line;
- p_line = NULL;
+ break_point_t *p_bp = NULL;
+ if( break_point.i_index > i_start )
+ p_bp = &break_point;
+ else if( break_point_fallback.i_index > i_start )
+ p_bp = &break_point_fallback;
- if( *psz_unicode == '\n')
+ if( p_bp )
{
- uint32_t *c_ptr;
-
- for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
- {
- *c_ptr = *(c_ptr+1);
- }
+ msg_Dbg( p_filter, "Breaking line");
+ for( int i = p_bp->i_index; i < i_index; i++ )
+ FT_Done_Glyph( (FT_Glyph)p_line->p_character[i - i_start].p_glyph );
+ p_line->i_character_count = p_bp->i_index - i_start;
+
+ i_index = p_bp->i_index;
+ pen = p_bp->pen;
+ line_bbox = p_bp->line_bbox;
+ i_face_height = p_bp->i_face_height;
+ i_ul_offset = p_bp->i_ul_offset;
+ i_ul_thickness = p_bp->i_ul_thickness;
+ }
+ else
+ {
+ msg_Err( p_filter, "Breaking unbreakable line");
}
+ break;
}
+
+ assert( p_line->i_character_count == i_index - i_start);
+ p_line->p_character[p_line->i_character_count++] = (line_character_t){
+ .p_glyph = (FT_BitmapGlyph)glyph,
+ .i_color = i_color,
+ .i_line_offset = i_line_offset,
+ .i_line_thickness = i_line_thickness,
+ };
+
+ pen.x = pen_new.x + p_current_face->glyph->advance.x;
+ pen.y = pen_new.y + p_current_face->glyph->advance.y;
+ line_bbox = line_bbox_new;
+ next:
+ i_glyph_last = i_glyph_index;
+ i_part_length--;
+ i_index++;
+
+ if( character == ' ' || character == '\t' )
+ SAVE_BP( break_point );
+ else if( character == 160 )
+ SAVE_BP( break_point_fallback );
}
- free( psz_unicode );
- if( p_face ) FT_Done_Face( p_face );
- i_prev = k;
+ if( b_break_line )
+ break;
}
- }
- free( pp_char_styles );
-#if defined(HAVE_FRIBIDI)
- free( psz_text );
-#endif
- if( p_line )
- {
- p_result->x = __MAX( p_result->x, tmp_result.x );
- p_result->y += tmp_result.y;
- }
+#undef SAVE_BP
+ /* Update our baseline */
+ if( i_face_height_previous > 0 )
+ i_base_line += __MAX(i_face_height, i_face_height_previous);
+ i_face_height_previous = i_face_height;
+
+ /* Update the line bbox with the actual base line */
+ if (line_bbox.yMax > line_bbox.yMin) {
+ line_bbox.yMin -= i_base_line;
+ line_bbox.yMax -= i_base_line;
+ }
+ BBoxEnlarge( &bbox, &line_bbox );
- if( pi_karaoke_bar )
- {
- int i = 0;
- for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
+ /* Terminate and append the line */
+ if( p_line )
{
- for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
+ p_line->i_width = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
+ p_line->i_base_line = i_base_line;
+ if( i_ul_thickness > 0 )
{
- if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
- {
- /* do nothing */
- }
- else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
- {
- /* 100% BG colour will render faster if we
- * instead make it 100% FG colour, so leave
- * the ratio alone and copy the value across
- */
- p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
- }
- else
+ for( int i = 0; i < p_line->i_character_count; i++ )
{
- if( pi_karaoke_bar[ i ] & 0x80 )
+ line_character_t *ch = &p_line->p_character[i];
+ if( ch->i_line_thickness < 0 )
{
- /* Swap Left and Right sides over for Right aligned
- * language text (eg. Arabic, Hebrew)
- */
- uint32_t i_tmp = p_line->p_fg_rgb[ k ];
-
- p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
- p_line->p_bg_rgb[ k ] = i_tmp;
+ ch->i_line_offset = i_ul_offset;
+ ch->i_line_thickness = i_ul_thickness;
}
- p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
}
}
- /* Jump over the '\n' at the line-end */
- i++;
+
+ *pp_line_next = p_line;
+ pp_line_next = &p_line->p_next;
+ }
+
+ *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
+
+ /* Skip what we have rendered and the line delimitor if present */
+ i_start = i_index;
+ if( i_start < i_len && psz_text[i_start] == '\n' )
+ i_start++;
+
+ if( bbox.yMax - bbox.yMin >= p_filter->fmt_out.video.i_visible_height )
+ {
+ msg_Err( p_filter, "Truncated too high subtitle" );
+ break;
}
- free( pi_karaoke_bar );
}
+ if( p_face )
+ FT_Done_Face( p_face );
+
+ free( pp_fribidi_styles );
+ free( p_fribidi_string );
+ free( pi_karaoke_bar );
+ *p_bbox = bbox;
return VLC_SUCCESS;
}
if( !b_html && !p_region_in->psz_text )
return VLC_EGENERIC;
- uint32_t *psz_text = calloc( strlen( b_html ? p_region_in->psz_html
- : p_region_in->psz_text ),
- sizeof( *psz_text ) );
- if( !psz_text )
- return VLC_EGENERIC;
+ const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
+ : p_region_in->psz_text );
+ uint32_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
+ text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
+ if( !psz_text || !pp_styles )
+ {
+ free( psz_text );
+ free( pp_styles );
+ return VLC_EGENERIC;
+ }
/* Reset the default fontsize in case screen metrics have changed */
p_filter->p_sys->i_font_size = GetFontSize( p_filter );
/* */
int rv = VLC_SUCCESS;
int i_text_length = 0;
- FT_Vector result = {0, 0};
+ FT_BBox bbox;
+ int i_max_face_height;
line_desc_t *p_lines = NULL;
+ uint32_t *pi_k_durations = NULL;
+
#ifdef HAVE_STYLES
if( b_html )
{
if( !p_xml_reader )
rv = VLC_EGENERIC;
- bool b_karaoke = false;
if( !rv )
{
/* Look for Root Node */
if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
{
- if( !strcasecmp( "karaoke", node ) )
+ if( strcasecmp( "karaoke", node ) == 0 )
{
- /* We're going to have to render the text a number
- * of times to show the progress marker on the text.
- */
- var_SetBool( p_filter, "text-rerender", true );
- b_karaoke = true;
+ pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
}
- else if( !strcasecmp( "text", node ) )
- {
- b_karaoke = false;
- }
- else
+ else if( strcasecmp( "text", node ) != 0 )
{
/* Only text and karaoke tags are supported */
msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
}
if( !rv )
{
- uint32_t i_runs = 0;
- uint32_t i_k_runs = 0;
- uint32_t *pi_run_lengths = NULL;
- uint32_t *pi_k_run_lengths = NULL;
- uint32_t *pi_k_durations = NULL;
- text_style_t **pp_styles = NULL;
-
- rv = ProcessNodes( p_filter, p_xml_reader,
- p_region_in->p_style, psz_text, &i_text_length,
- &i_runs, &pi_run_lengths, &pp_styles,
- b_karaoke, &i_k_runs, &pi_k_run_lengths,
- &pi_k_durations );
-
- if( !rv && i_text_length > 0 )
- {
- rv = ProcessLines( p_filter, psz_text, i_text_length, i_runs,
- pi_run_lengths, pp_styles, &p_lines,
- &result, b_karaoke, i_k_runs,
- pi_k_run_lengths, pi_k_durations );
- }
-
- for( uint32_t k = 0; k < i_runs; k++ )
- text_style_Delete( pp_styles[k] );
- free( pp_styles );
- free( pi_run_lengths );
-
+ rv = ProcessNodes( p_filter,
+ psz_text, pp_styles, pi_k_durations, &i_text_length,
+ p_xml_reader, p_region_in->p_style );
}
if( p_xml_reader )
else
#endif
{
-
- size_t i_iconv_length;
- IconvText( p_filter, p_region_in->psz_text, &i_iconv_length, psz_text );
- i_text_length = i_iconv_length;
-
text_style_t *p_style;
if( p_region_in->p_style )
p_style = CreateStyle( p_region_in->p_style->psz_fontname,
(p_region_in->p_style->i_font_color & 0xffffff) |
((p_region_in->p_style->i_font_alpha & 0xff) << 24),
0x00ffffff,
- p_region_in->p_style->i_style_flags & STYLE_BOLD,
- p_region_in->p_style->i_style_flags & STYLE_ITALIC,
- p_region_in->p_style->i_style_flags & STYLE_UNDERLINE,
- p_region_in->p_style->i_style_flags & STYLE_STRIKEOUT);
+ p_region_in->p_style->i_style_flags & (STYLE_BOLD |
+ STYLE_ITALIC |
+ STYLE_UNDERLINE |
+ STYLE_STRIKEOUT) );
else
p_style = CreateStyle( p_sys->psz_fontfamily,
p_sys->i_font_size,
(p_sys->i_font_color & 0xffffff) |
(((255-p_sys->i_font_opacity) & 0xff) << 24),
- 0x00ffffff,
- false, false, false, false );
- uint32_t i_run_length = i_text_length;
+ 0x00ffffff, 0);
- rv = ProcessLines( p_filter, psz_text, i_text_length,
- 1, &i_run_length, &p_style,
- &p_lines, &result,
- false, 0, NULL, NULL );
- text_style_Delete( p_style );
+ i_text_length = SetupText( p_filter,
+ psz_text,
+ pp_styles,
+ NULL,
+ p_region_in->psz_text, p_style, 0 );
+ }
+
+ if( !rv && i_text_length > 0 )
+ {
+ rv = ProcessLines( p_filter,
+ &p_lines, &bbox, &i_max_face_height,
+ psz_text, pp_styles, pi_k_durations, i_text_length );
}
- free( psz_text );
p_region_out->i_x = p_region_in->i_x;
p_region_out->i_y = p_region_in->i_y;
/* Don't attempt to render text that couldn't be layed out
* properly. */
- if( !rv && i_text_length > 0 )
+ if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
{
if( var_InheritBool( p_filter, "freetype-yuvp" ) )
- RenderYUVP( p_filter, p_region_out, p_lines,
- result.x, result.y );
+ RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
else
- RenderYUVA( p_filter, p_region_out, p_lines,
- result.x, result.y );
+ RenderYUVA( p_filter, p_region_out,
+ p_lines,
+ &bbox,
+ p_sys->i_background_opacity > 0 ? i_max_face_height / 4 : 0 );
+
+ /* With karaoke, we're going to have to render the text a number
+ * of times to show the progress marker on the text.
+ */
+ if( pi_k_durations )
+ var_SetBool( p_filter, "text-rerender", true );
}
FreeLines( p_lines );
+
+ free( psz_text );
+ for( int i = 0; i < i_text_length; i++ )
+ {
+ if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
+ text_style_Delete( pp_styles[i] );
+ }
+ free( pp_styles );
+ free( pi_k_durations );
+
return rv;
}
#endif
-static void FreeLine( line_desc_t *p_line )
-{
- unsigned int i;
- for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
- {
- FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
- }
- free( p_line->pp_glyphs );
- free( p_line->p_glyph_pos );
- free( p_line->p_fg_rgb );
- free( p_line->p_bg_rgb );
- free( p_line->p_fg_bg_ratio );
- free( p_line->pi_underline_offset );
- free( p_line->pi_underline_thickness );
- free( p_line );
-}
-
-static void FreeLines( line_desc_t *p_lines )
-{
- line_desc_t *p_line, *p_next;
-
- if( !p_lines ) return;
-
- for( p_line = p_lines; p_line != NULL; p_line = p_next )
- {
- p_next = p_line->p_next;
- FreeLine( p_line );
- }
-}
-
-static line_desc_t *NewLine( int i_count )
-{
- line_desc_t *p_line = malloc( sizeof(line_desc_t) );
-
- if( !p_line ) return NULL;
- p_line->i_height = 0;
- p_line->i_width = 0;
- p_line->p_next = NULL;
-
- p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
- p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
- p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
- p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
- p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
- p_line->pi_underline_offset = calloc( i_count + 1, sizeof( int ) );
- p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
- if( ( p_line->pp_glyphs == NULL ) ||
- ( p_line->p_glyph_pos == NULL ) ||
- ( p_line->p_fg_rgb == NULL ) ||
- ( p_line->p_bg_rgb == NULL ) ||
- ( p_line->p_fg_bg_ratio == NULL ) ||
- ( p_line->pi_underline_offset == NULL ) ||
- ( p_line->pi_underline_thickness == NULL ) )
- {
- free( p_line->pi_underline_thickness );
- free( p_line->pi_underline_offset );
- free( p_line->p_fg_rgb );
- free( p_line->p_bg_rgb );
- free( p_line->p_fg_bg_ratio );
- free( p_line->p_glyph_pos );
- free( p_line->pp_glyphs );
- free( p_line );
- return NULL;
- }
- p_line->pp_glyphs[0] = NULL;
- p_line->b_new_color_mode = false;
-
- return p_line;
-}
-
/*****************************************************************************
* Create: allocates osd-text video thread output method
*****************************************************************************
int i_error = 0, fontindex = 0;
/* Allocate structure */
- p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
+ p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
if( !p_sys )
return VLC_ENOMEM;
psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
- p_sys->i_effect = var_InheritInteger( p_filter, "freetype-effect" );
p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
+ p_sys->i_background_opacity = var_InheritInteger( p_filter,"freetype-background-opacity" );;
+ p_sys->i_background_opacity = __MAX( __MIN( p_sys->i_background_opacity, 255 ), 0 );
+ p_sys->i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
+ p_sys->i_background_color = __MAX( __MIN( p_sys->i_background_color, 0xFFFFFF ), 0 );
+
#ifdef WIN32
/* Get Windows Font folder */
wchar_t wdir[MAX_PATH];
goto error;
}
- p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
-
if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;