X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fmisc%2Ftext_renderer%2Ffreetype.c;h=c431748c94fe491850da39c51a0d184c4afa440e;hb=be03564a2edcd4ab4de3a86c0f2aa64bc1c60d39;hp=6be158a3ccb1f197cdd5ccd3e4918428a90ad787;hpb=d30ebaf79b24def2f33c55fae882559ce674260b;p=vlc diff --git a/modules/misc/text_renderer/freetype.c b/modules/misc/text_renderer/freetype.c index 6be158a3cc..c431748c94 100644 --- a/modules/misc/text_renderer/freetype.c +++ b/modules/misc/text_renderer/freetype.c @@ -62,6 +62,8 @@ #include #include FT_FREETYPE_H #include FT_GLYPH_H +#include FT_STROKER_H + #define FT_FLOOR(X) ((X & -64) >> 6) #define FT_CEIL(X) (((X + 63) & -64) >> 6) #ifndef FT_MulFix @@ -119,23 +121,19 @@ static void Destroy( vlc_object_t * ); "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") + +#define OUTLINE_OPACITY_TEXT N_("Outline opacity") +#define OUTLINE_COLOR_TEXT N_("Outline color") +#define OUTLINE_THICKNESS_TEXT N_("Outline thickness") + 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, @@ -147,6 +145,13 @@ static const char *const ppsz_color_descriptions[] = { N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") }; +static const int pi_outline_thickness[] = { + 0, 2, 4, 6, +}; +static const char *const ppsz_outline_thickness[] = { + N_("None"), N_("Thin"), N_("Normal"), N_("Thick"), +}; + vlc_module_begin () set_shortname( N_("Text renderer")) set_description( N_("Freetype2 font renderer") ) @@ -163,6 +168,11 @@ vlc_module_begin () FONTSIZE_LONGTEXT, true ) change_safe() + add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT, + FONTSIZER_LONGTEXT, false ) + change_integer_list( pi_sizes, ppsz_sizes_text ) + change_safe() + /* opacity valid on 0..255, with default 255 = fully opaque */ add_integer_with_range( "freetype-opacity", 255, 0, 255, OPACITY_TEXT, OPACITY_LONGTEXT, false ) @@ -174,16 +184,28 @@ vlc_module_begin () 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 ) + 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-effect", 2, EFFECT_TEXT, - EFFECT_LONGTEXT, false ) - change_integer_list( pi_effects, ppsz_effects_text ) + add_integer_with_range( "freetype-outline-opacity", 255, 0, 255, + OUTLINE_OPACITY_TEXT, "", false ) + change_safe() + add_integer( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT, + "", false ) + change_integer_list( pi_color_values, ppsz_color_descriptions ) + change_safe() + add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT, + "", false ) + change_integer_list( pi_outline_thickness, ppsz_outline_thickness ) change_safe() + add_obsolete_integer( "freetype-effect" ); + add_bool( "freetype-yuvp", false, YUVP_TEXT, YUVP_LONGTEXT, true ) set_capability( "text renderer", 100 ) @@ -196,24 +218,24 @@ vlc_module_end () * Local prototypes *****************************************************************************/ +typedef struct +{ + FT_BitmapGlyph p_glyph; + FT_BitmapGlyph p_outline; + 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 */ - uint32_t *pi_color; - /** underline information -- only supplied if text should be underlined */ - int *pi_underline_offset; - uint16_t *pi_underline_thickness; - - int i_width; - - int i_alpha; - line_desc_t *p_next; + + int i_width; + int i_base_line; + int i_character_count; + line_character_t *p_character; }; typedef struct font_stack_t font_stack_t; @@ -237,10 +259,17 @@ struct filter_sys_t { FT_Library p_library; /* handle to library */ FT_Face p_face; /* handle to face object */ + FT_Stroker p_stroker; 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; + + double f_outline_thickness; + uint8_t i_outline_opacity; + int i_outline_color; int i_default_font_size; int i_display_height; @@ -565,7 +594,8 @@ static char* Win32_Select( filter_t *p_filter, const char* family, * 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] = @@ -578,17 +608,11 @@ static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region, 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 ); @@ -599,7 +623,8 @@ static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region, /* Calculate text color components * Only use the first color */ - YUVFromRGB( p_line->pi_color[ 0 ], &i_y, &i_u, &i_v ); + 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; @@ -610,7 +635,7 @@ static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region, 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++ ) { @@ -619,7 +644,7 @@ static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region, 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; @@ -630,44 +655,32 @@ static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region, 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; } } } @@ -699,285 +712,167 @@ static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region, 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; + int i_an = i_a * i_alpha / 255; - 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; + 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]; - 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; - - 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; + 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; - /* Save the alpha value */ - 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 */ - 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 ); - - if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND ) - memset( p_dst_a, 0x00, i_pitch * p_region->fmt.i_height ); - else - 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 outline glyphs in the first pass, and then the normal glyphs */ + for( int g = 0; g < 2; g++ ) { - 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 ) + /* Render all lines */ + for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next ) { - if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) + int i_align_left = i_margin; + if( p_line->i_width < i_text_width ) { - i_align_offset = i_width - p_line->i_width; + /* Left offset to take into account alignment */ + if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) + i_align_left += ( i_text_width - p_line->i_width ); + else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) + i_align_left += ( i_text_width - p_line->i_width ) / 2; } - 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 ]; + int i_align_top = i_margin; - 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; - - /* Every glyph can (and in fact must) have its own color */ - uint8_t i_y, i_u, i_v; - YUVFromRGB( p_line->pi_color[ i ], &i_y, &i_u, &i_v ); - - for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ ) + /* Render all glyphs and underline/strikethrough */ + for( int i = 0; i < p_line->i_character_count; i++ ) { - for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ ) + const line_character_t *ch = &p_line->p_character[i]; + FT_BitmapGlyph p_glyph = g == 0 ? ch->p_outline : ch->p_glyph; + if( !p_glyph ) + continue; + + uint32_t i_color = ch->i_color; + i_a = 0xff - ((i_color >> 24) & 0xff); + if( g == 0 ) { - 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_a = i_a * p_sys->i_outline_opacity / 255; + i_color = p_sys->i_outline_color; } - 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); + YUVFromRGB( 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 ); + + /* underline/strikethrough are only rendered for the normal glyph */ + if( g == 1 && 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; } @@ -1561,14 +1456,15 @@ static int ProcessNodes( filter_t *p_filter, static void FreeLine( line_desc_t *p_line ) { - for( int i = 0; p_line->pp_glyphs && 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->pi_color ); - free( p_line->pi_underline_offset ); - free( p_line->pi_underline_thickness ); + for( int i = 0; i < p_line->i_character_count; i++ ) + { + line_character_t *ch = &p_line->p_character[i]; + FT_Done_Glyph( (FT_Glyph)ch->p_glyph ); + if( ch->p_outline ) + FT_Done_Glyph( (FT_Glyph)ch->p_outline ); + } + + free( p_line->p_character ); free( p_line ); } @@ -1589,25 +1485,17 @@ static line_desc_t *NewLine( int i_count ) if( !p_line ) return NULL; - p_line->i_width = 0; - p_line->i_alpha = 0xff; - p_line->p_next = NULL; + p_line->i_width = 0; + p_line->i_base_line = 0; + p_line->i_character_count = 0; - p_line->pp_glyphs = calloc( i_count + 1, sizeof(*p_line->pp_glyphs) ); - p_line->p_glyph_pos = calloc( i_count + 1, sizeof(*p_line->p_glyph_pos) ); - p_line->pi_color = calloc( i_count + 1, sizeof(*p_line->pi_color) ); - p_line->pi_underline_offset = calloc( i_count + 1, sizeof(*p_line->pi_underline_offset) ); - p_line->pi_underline_thickness = calloc( i_count + 1, sizeof(*p_line->pi_underline_thickness) ); - - if( !p_line->pp_glyphs || !p_line->p_glyph_pos || - !p_line->pi_color || - !p_line->pi_underline_offset || !p_line->pi_underline_thickness ) + p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) ); + if( !p_line->p_character ) { - FreeLine( p_line ); + free( p_line ); return NULL; } - p_line->pp_glyphs[0] = NULL; return p_line; } @@ -1719,12 +1607,13 @@ static bool FaceStyleEquals( const text_style_t *p_style1, } static int GetGlyph( filter_t *p_filter, - FT_Glyph *pp_glyph, - FT_BBox *p_bbox, + FT_Glyph *pp_glyph, FT_BBox *p_glyph_bbox, + FT_Glyph *pp_outline, FT_BBox *p_outline_bbox, FT_Face p_face, int i_glyph_index, - int i_style_flags ) + 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 ) ) @@ -1749,23 +1638,58 @@ static int GetGlyph( filter_t *p_filter, return VLC_EGENERIC; } - FT_BBox bbox; - FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, &bbox ); + FT_Glyph outline = NULL; + if( p_filter->p_sys->p_stroker ) + { + outline = glyph; + FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ); + FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 ); - if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1) ) + FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox ); + } + *pp_outline = outline; + + if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) ) { FT_Done_Glyph( glyph ); return VLC_EGENERIC; } + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox ); *pp_glyph = glyph; - *p_bbox = bbox; + return VLC_SUCCESS; } +static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen ) +{ + FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph; + if( p_bbox->xMin >= p_bbox->xMax ) + { + p_bbox->xMin = FT_CEIL(p_pen->x); + p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x); + glyph_bmp->left = p_bbox->xMin; + } + if( p_bbox->yMin >= p_bbox->yMax ) + { + p_bbox->yMax = FT_CEIL(p_pen->y); + p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y); + glyph_bmp->top = p_bbox->yMax; + } +} + +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_Vector *p_size, + FT_BBox *p_bbox, + int *pi_max_face_height, uint32_t *psz_text, text_style_t **pp_styles, @@ -1861,16 +1785,18 @@ static int ProcessLines( filter_t *p_filter, } free( p_new_positions ); + *pi_max_face_height = 0; *pp_lines = NULL; line_desc_t **pp_line_next = pp_lines; FT_BBox bbox = { - .xMin = 0, - .yMin = 0, - .xMax = 0, - .yMax = 0, + .xMin = INT_MAX, + .yMin = INT_MAX, + .xMax = INT_MIN, + .yMax = INT_MIN, }; - FT_Vector pen = { .x = 0, .y = 0 }; + 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; ) @@ -1883,19 +1809,26 @@ static int ProcessLines( filter_t *p_filter, /* 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; - pen.x = 0; + FT_Vector pen = { + .x = 0, + .y = 0, + }; int i_face_height = 0; FT_BBox line_bbox = { - .xMin = 0, - .yMin = 0, - .xMax = 0, - .yMax = 0, + .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; @@ -1905,6 +1838,8 @@ static int ProcessLines( filter_t *p_filter, 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 ); @@ -1938,10 +1873,19 @@ static int ProcessLines( filter_t *p_filter, { 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 ); + if( p_sys->p_stroker ) + { + int i_radius = (p_current_style->i_font_size << 6) * p_sys->f_outline_thickness; + FT_Stroker_Set( p_sys->p_stroker, + i_radius, + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_ROUND, 0 ); + } } p_previous_style = p_current_style; - i_face_height = __MAX(i_face_height, FT_CEIL(p_current_face->size->metrics.height)); + 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; @@ -1958,53 +1902,71 @@ static int ProcessLines( filter_t *p_filter, 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 ) ) + FT_Glyph outline; + FT_BBox outline_bbox; + if( GetGlyph( p_filter, + &glyph, &glyph_bbox, + &outline, &outline_bbox, + p_current_face, i_glyph_index, p_glyph_style->i_style_flags, &pen_new ) ) goto next; - FT_Vector glyph_pos = { - .x = pen.x + FT_CEIL(kerning.x), - .y = pen.y - }; + FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new ); + if( outline ) + FixGlyph( outline, &outline_bbox, p_current_face, &pen_new ); + /* FIXME and what about outline */ + 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_font_color; - int i_ul_offset = 0; - int i_ul_thickness = 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_ul_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position, - p_current_face->size->metrics.y_scale)) ); - - i_ul_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness, + 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)) ); + if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT ) { /* Move the baseline to make it strikethrough instead of * underline. That means that strikethrough takes precedence */ - i_ul_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2, - p_current_face->size->metrics.y_scale)) ); + i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2, + p_current_face->size->metrics.y_scale)) ); + } + else if( i_line_thickness > 0 ) + { + glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness ); + + /* 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_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph; - FT_BBox line_bbox_new = { - .xMin = 0, - .xMax = __MAX( line_bbox.xMax, - glyph_pos.x + glyph_bbox.xMax - glyph_bbox.xMin + glyph_bmp->left ), - .yMin = 0, - .yMax = __MAX( line_bbox.yMax, - glyph_pos.y + glyph_bbox.yMax - glyph_bbox.yMin + glyph_bmp->top ), - }; + FT_BBox line_bbox_new = line_bbox; + BBoxEnlarge( &line_bbox_new, &glyph_bbox ); + if( outline ) + BBoxEnlarge( &line_bbox_new, &outline_bbox ); b_break_line = i_index > i_start && - line_bbox_new.xMax >= p_filter->fmt_out.video.i_visible_width; + line_bbox_new.xMax - line_bbox_new.xMin >= p_filter->fmt_out.video.i_visible_width; if( b_break_line ) { FT_Done_Glyph( glyph ); + if( outline ) + FT_Done_Glyph( outline ); break_point_t *p_bp = NULL; if( break_point.i_index > i_start ) @@ -2017,13 +1979,19 @@ static int ProcessLines( filter_t *p_filter, msg_Dbg( p_filter, "Breaking line"); for( int i = p_bp->i_index; i < i_index; i++ ) { - FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[i - i_start] ); - p_line->pp_glyphs[i - i_start] = NULL; + line_character_t *ch = &p_line->p_character[i - i_start]; + FT_Done_Glyph( (FT_Glyph)ch->p_glyph ); + if( ch->p_outline ) + FT_Done_Glyph( (FT_Glyph)ch->p_outline ); } + 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 { @@ -2032,14 +2000,17 @@ static int ProcessLines( filter_t *p_filter, break; } - int i_line_index = i_index - i_start; - p_line->pp_glyphs[i_line_index] = (FT_BitmapGlyph)glyph; - p_line->p_glyph_pos[i_line_index] = glyph_pos; - p_line->pi_color[i_line_index] = i_color; /* FIXME alpha per glyph */ - p_line->pi_underline_offset[i_line_index] = i_ul_offset; - p_line->pi_underline_thickness[i_line_index] = i_ul_thickness; + 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, + .p_outline = (FT_BitmapGlyph)outline, + .i_color = i_color, + .i_line_offset = i_line_offset, + .i_line_thickness = i_line_thickness, + }; - pen.x += FT_CEIL(kerning.x) + FT_CEIL(p_current_face->glyph->advance.x); + 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; @@ -2055,26 +2026,48 @@ static int ProcessLines( filter_t *p_filter, break; } #undef SAVE_BP - bbox.xMax = __MAX(bbox.xMax, line_bbox.xMax); - bbox.yMax = __MAX(bbox.yMax, line_bbox.yMax); - - pen.y += i_face_height; + /* 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 ); /* Terminate and append the line */ if( p_line ) { - p_line->i_alpha = 0x00; - p_line->i_width = line_bbox.xMax - line_bbox.xMin; + 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 ) + { + for( int i = 0; i < p_line->i_character_count; i++ ) + { + line_character_t *ch = &p_line->p_character[i]; + if( ch->i_line_thickness < 0 ) + { + ch->i_line_offset = i_ul_offset; + ch->i_line_thickness = i_ul_thickness; + } + } + } + *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 >= p_filter->fmt_out.video.i_visible_height ) + if( bbox.yMax - bbox.yMin >= p_filter->fmt_out.video.i_visible_height ) { msg_Err( p_filter, "Truncated too high subtitle" ); break; @@ -2087,8 +2080,7 @@ static int ProcessLines( filter_t *p_filter, free( p_fribidi_string ); free( pi_karaoke_bar ); - p_size->x = bbox.xMax - bbox.xMin; - p_size->y = bbox.yMax - bbox.yMin; + *p_bbox = bbox; return VLC_SUCCESS; } @@ -2128,7 +2120,8 @@ static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out, /* */ 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; @@ -2221,7 +2214,7 @@ static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out, if( !rv && i_text_length > 0 ) { rv = ProcessLines( p_filter, - &p_lines, &result, + &p_lines, &bbox, &i_max_face_height, psz_text, pp_styles, pi_k_durations, i_text_length ); } @@ -2230,15 +2223,15 @@ static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out, /* Don't attempt to render text that couldn't be layed out * properly. */ - if( !rv && i_text_length > 0 && result.x > 0 && result.y > 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. @@ -2309,12 +2302,23 @@ static int Create( vlc_object_t *p_this ) 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 ); + + p_sys->f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0; + p_sys->f_outline_thickness = __MAX( __MIN( p_sys->f_outline_thickness, 0.5 ), 0.0 ); + p_sys->i_outline_opacity = var_InheritInteger( p_filter, "freetype-outline-opacity" ); + p_sys->i_outline_opacity = __MAX( __MIN( p_sys->i_outline_opacity, 255 ), 0 ); + p_sys->i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" ); + p_sys->i_outline_color = __MAX( __MIN( p_sys->i_outline_color, 0xFFFFFF ), 0 ); + #ifdef WIN32 /* Get Windows Font folder */ wchar_t wdir[MAX_PATH]; @@ -2404,6 +2408,13 @@ static int Create( vlc_object_t *p_this ) if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error; + p_sys->p_stroker = NULL; + if( p_sys->f_outline_thickness > 0.001 ) + { + i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker ); + if( i_error ) + msg_Err( p_filter, "Failed to create stroker for outlining" ); + } p_sys->pp_font_attachments = NULL; p_sys->i_font_attachments = 0; @@ -2457,6 +2468,8 @@ static void Destroy( vlc_object_t *p_this ) * even if no other library functions have been made since FcInit(), * so don't call it. */ + if( p_sys->p_stroker ) + FT_Stroker_Done( p_sys->p_stroker ); FT_Done_Face( p_sys->p_face ); FT_Done_FreeType( p_sys->p_library ); free( p_sys );