* freetype.c : Put text on the video, using freetype2
*****************************************************************************
* Copyright (C) 2002, 2003 VideoLAN
- * $Id: freetype.c,v 1.1 2003/07/14 21:32:59 sigmunau Exp $
+ * $Id: freetype.c,v 1.14 2003/07/26 18:54:20 titer Exp $
*
* Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
*
#include FT_FREETYPE_H
#include FT_GLYPH_H
-#define FT_RENDER_MODE_NORMAL 0 /* Why do we have to do that ? */
+#ifdef SYS_DARWIN
+#define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
+#elif defined( SYS_BEOS )
+#define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
+#else
+#define DEFAULT_FONT ""
+#endif
/*****************************************************************************
* Local prototypes
static int Create ( vlc_object_t * );
static void Destroy ( vlc_object_t * );
-static void Render ( vout_thread_t *, picture_t *,
- const subpicture_t * );
-static int AddText ( vout_thread_t *, byte_t *, text_style_t *, int,
- int, int, mtime_t, mtime_t );
-static int GetUnicodeCharFromUTF8( byte_t ** );
+static void Render ( vout_thread_t *, picture_t *,
+ const subpicture_t * );
+static void RenderI420( vout_thread_t *, picture_t *,
+ const subpicture_t * );
+static void RenderYUY2( vout_thread_t *, picture_t *,
+ const subpicture_t * );
+static int AddText ( vout_thread_t *, byte_t *, text_style_t *, int,
+ int, int, mtime_t, mtime_t );
+static int GetUnicodeCharFromUTF8( byte_t ** );
+static void FreeString( subpicture_t * );
/*****************************************************************************
* Module descriptor
vlc_module_begin();
add_category_hint( N_("Fonts"), NULL, VLC_FALSE );
- add_file( "freetype-font", "", NULL, FONT_TEXT, FONT_LONGTEXT, VLC_FALSE );
+ add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT, VLC_FALSE );
add_integer( "freetype-fontsize", 16, NULL, FONTSIZE_TEXT, FONTSIZE_LONGTEXT, VLC_FALSE );
set_description( _("freetype2 font renderer") );
set_capability( "text renderer", 100 );
vlc_module_end();
/**
- Describes a string to be displayed on the video, or a linked list of
- such
-*/
+ * Private data in a aubpicture. Describes a string.
+ */
struct subpicture_sys_t
{
int i_x_margin;
/** The string associated with this subpicture */
byte_t *psz_text;
/** NULL-terminated list of glyphs making the string */
- FT_Glyph *pp_glyphs;
+ FT_BitmapGlyph *pp_glyphs;
/** list of relative positions for the glyphs */
FT_Vector *p_glyph_pos;
};
/*****************************************************************************
- * vout_sys_t: osd_text local data
+ * text_remderer_sys_t: freetype local data
*****************************************************************************
* This structure is part of the video output thread descriptor.
- * It describes the osd-text specific properties of an output thread.
+ * It describes the freetype specific properties of an output thread.
*****************************************************************************/
struct text_renderer_sys_t
{
vlc_bool_t i_use_kerning;
uint8_t pi_gamma[256];
};
-/* more prototypes */
-//static void ComputeBoundingBox( subpicture_sys_t * );
-static void FreeString( subpicture_t * );
/*****************************************************************************
* Create: allocates osd-text video thread output method
char *psz_fontfile;
int i, i_error;
double gamma_inv = 1.0f / gamma_value;
-
+ vlc_value_t val;
+
/* Allocate structure */
p_vout->p_text_renderer_data = malloc( sizeof( text_renderer_sys_t ) );
if( p_vout->p_text_renderer_data == NULL )
for (i = 0; i < 256; i++) {
p_vout->p_text_renderer_data->pi_gamma[i] =
(uint8_t)( pow( (double)i / 255.0f, gamma_inv) * 255.0f );
- //msg_Dbg( p_vout, "%d", p_vout->p_text_renderer_data->pi_gamma[i]);
}
+ var_Create( p_vout, "freetype-font", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ var_Create( p_vout, "freetype-fontsize",
+ VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+
/* Look what method was requested */
- psz_fontfile = config_GetPsz( p_vout, "freetype-font" );
+ var_Get( p_vout, "freetype-font", &val );
+ psz_fontfile = val.psz_string;
+
+ if( !psz_fontfile || !*psz_fontfile )
+ {
+ if( psz_fontfile ) free( psz_fontfile );
+ psz_fontfile = (char *)malloc( PATH_MAX + 1 );
+#ifdef WIN32
+ GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
+ strcat( psz_fontfile, "\\fonts\\arial.ttf" );
+#elif SYS_DARWIN
+ strcpy( psz_fontfile, DEFAULT_FONT );
+#else
+ msg_Err( p_vout, "user didn't specify a font" );
+ free( p_vout->p_text_renderer_data );
+ return VLC_EGENERIC;
+#endif
+ }
+
i_error = FT_Init_FreeType( &p_vout->p_text_renderer_data->p_library );
if( i_error )
{
free( p_vout->p_text_renderer_data );
return VLC_EGENERIC;
}
+
i_error = FT_New_Face( p_vout->p_text_renderer_data->p_library,
- psz_fontfile, 0,
+ psz_fontfile ? psz_fontfile : "", 0,
&p_vout->p_text_renderer_data->p_face );
if( i_error == FT_Err_Unknown_File_Format )
{
msg_Err( p_vout, "file %s have unknown format", psz_fontfile );
- FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
+ FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
free( p_vout->p_text_renderer_data );
+ if( psz_fontfile ) free( psz_fontfile );
return VLC_EGENERIC;
}
else if( i_error )
{
- msg_Err( p_vout, "failed to load font file" );
- FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
+ msg_Err( p_vout, "failed to load font file %s", psz_fontfile );
+ FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
free( p_vout->p_text_renderer_data );
+ if( psz_fontfile ) free( psz_fontfile );
return VLC_EGENERIC;
}
+ if( psz_fontfile ) free( psz_fontfile );
+
i_error = FT_Select_Charmap( p_vout->p_text_renderer_data->p_face,
- FT_ENCODING_UNICODE );
+ ft_encoding_unicode );
if ( i_error )
{
- msg_Err( p_vout, "Font has no unicode translation table" );
- FT_Done_Face( p_vout->p_text_renderer_data->p_face );
- FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
- free( p_vout->p_text_renderer_data );
- return VLC_EGENERIC;
+ msg_Err( p_vout, "Font has no unicode translation table" );
+ FT_Done_Face( p_vout->p_text_renderer_data->p_face );
+ FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
+ free( p_vout->p_text_renderer_data );
+ return VLC_EGENERIC;
}
-
- p_vout->p_text_renderer_data->i_use_kerning = FT_HAS_KERNING(p_vout->p_text_renderer_data->p_face);
-
- i_error = FT_Set_Pixel_Sizes( p_vout->p_text_renderer_data->p_face, 0,
- config_GetInt( p_vout, "freetype-fontsize" ) );
+
+ p_vout->p_text_renderer_data->i_use_kerning =
+ FT_HAS_KERNING(p_vout->p_text_renderer_data->p_face);
+ var_Get( p_vout, "freetype-fontsize", &val );
+
+ i_error = FT_Set_Pixel_Sizes( p_vout->p_text_renderer_data->p_face, 0, val.i_int );
if( i_error )
{
- msg_Err( p_vout, "couldn't set font size to %d",
- config_GetInt( p_vout, "osd-fontsize" ) );
+ msg_Err( p_vout, "couldn't set font size to %d", val.i_int );
free( p_vout->p_text_renderer_data );
return VLC_EGENERIC;
}
* Clean up all data and library connections
*****************************************************************************/
static void Destroy( vlc_object_t *p_this )
-{
+{
vout_thread_t *p_vout = (vout_thread_t *)p_this;
FT_Done_Face( p_vout->p_text_renderer_data->p_face );
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
*****************************************************************************
* This function merges the previously rendered freetype glyphs into a picture
*****************************************************************************/
-static void Render( vout_thread_t *p_vout, picture_t *p_pic,
- const subpicture_t *p_subpic )
+static void Render( vout_thread_t *p_vout, picture_t *p_pic,
+ const subpicture_t *p_subpic )
+{
+ switch( p_vout->output.i_chroma )
+ {
+ /* I420 target, no scaling */
+ case VLC_FOURCC('I','4','2','0'):
+ case VLC_FOURCC('I','Y','U','V'):
+ case VLC_FOURCC('Y','V','1','2'):
+ RenderI420( p_vout, p_pic, p_subpic );
+ break;
+#if 0
+ /* RV16 target, scaling */
+ case VLC_FOURCC('R','V','1','6'):
+ RenderRV16( p_vout, p_pic, p_subpic );
+ break;
+
+ /* RV32 target, scaling */
+ case VLC_FOURCC('R','V','2','4'):
+ case VLC_FOURCC('R','V','3','2'):
+ RenderRV32( p_vout, p_pic, p_subpic );
+ break;
+#endif
+ /* NVidia or BeOS overlay, no scaling */
+ case VLC_FOURCC('Y','U','Y','2'):
+ RenderYUY2( p_vout, p_pic, p_subpic );
+ break;
+
+ default:
+ msg_Err( p_vout, "unknown chroma, can't render SPU" );
+ break;
+ }
+}
+
+/**
+ * Draw a string on a i420 (or similar) picture
+ */
+static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
+ const subpicture_t *p_subpic )
{
subpicture_sys_t *p_string = p_subpic->p_sys;
- int i_plane, i_error,x,y,pen_x, pen_y;
+ int i_plane, x, y, pen_x, pen_y;
unsigned int i;
-
+
for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
{
uint8_t *p_in;
p_in = p_pic->p[i_plane].p_pixels;
if ( i_plane == 0 )
- {
- if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
- {
- pen_y = p_pic->p[i_plane].i_lines - p_string->i_height -
- p_string->i_y_margin;
- }
- else
- {
- pen_y = p_string->i_y_margin;
- }
- if ( p_string->i_flags & OSD_ALIGN_RIGHT )
- {
- pen_x = i_pitch - p_string->i_width
- - p_string->i_x_margin;
- }
- else
- {
- pen_x = p_string->i_x_margin;
- }
-
- for( i = 0; p_string->pp_glyphs[i] != NULL; i++ )
- {
- if( p_string->pp_glyphs[i] )
- {
- FT_Glyph p_glyph = p_string->pp_glyphs[i];
- FT_BitmapGlyph p_image;
- i_error = FT_Glyph_To_Bitmap( &p_glyph,
- FT_RENDER_MODE_NORMAL,
- &p_string->p_glyph_pos[i],
- 0 );
- if ( i_error ) continue;
- p_image = (FT_BitmapGlyph)p_glyph;
-#define alpha p_vout->p_text_renderer_data->pi_gamma[p_image->bitmap.buffer[x+ y*p_image->bitmap.width]]
-#define pixel p_in[(p_string->p_glyph_pos[i].y + pen_y + y - p_image->top)*i_pitch+x+pen_x+p_string->p_glyph_pos[i].x+p_image->left]
- for(y = 0; y < p_image->bitmap.rows; y++ )
- {
- for( x = 0; x < p_image->bitmap.width; x++ )
- {
- // pixel = alpha;
- // pixel = (pixel^alpha)^pixel;
- pixel = ((pixel*(255-alpha))>>8) + (255*alpha>>8);
- }
- }
- FT_Done_Glyph( p_glyph );
- }
- }
- }
+ {
+ if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
+ {
+ pen_y = p_pic->p[i_plane].i_lines - p_string->i_height -
+ p_string->i_y_margin;
+ }
+ else
+ {
+ pen_y = p_string->i_y_margin;
+ }
+ pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.height / 100;
+ if ( p_string->i_flags & OSD_ALIGN_RIGHT )
+ {
+ pen_x = i_pitch - p_string->i_width
+ - p_string->i_x_margin;
+ }
+ else
+ {
+ pen_x = p_string->i_x_margin;
+ }
+
+ for( i = 0; p_string->pp_glyphs[i] != NULL; i++ )
+ {
+ if( p_string->pp_glyphs[i] )
+ {
+ FT_BitmapGlyph p_glyph = p_string->pp_glyphs[ i ];
+#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
+#define pixel p_in[ ( p_string->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pitch+x + pen_x + p_string->p_glyph_pos[ i ].x + p_glyph->left ]
+ for(y = 0; y < p_glyph->bitmap.rows; y++ )
+ {
+ for( x = 0; x < p_glyph->bitmap.width; x++ )
+ {
+ pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
+ ( 255 * alpha >> 8 );
+#undef alpha
+#undef pixel
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
+ {
+ pen_y = p_pic->p[i_plane].i_lines - ( p_string->i_height>>1) -
+ (p_string->i_y_margin>>1);
+ }
+ else
+ {
+ pen_y = p_string->i_y_margin >> 1;
+ }
+ pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.height / 200;
+ if ( p_string->i_flags & OSD_ALIGN_RIGHT )
+ {
+ pen_x = i_pitch - ( p_string->i_width >> 1 )
+ - ( p_string->i_x_margin >> 1 );
+ }
+ else
+ {
+ pen_x = p_string->i_x_margin >> 1;
+ }
+
+ for( i = 0; p_string->pp_glyphs[i] != NULL; i++ )
+ {
+ if( p_string->pp_glyphs[i] )
+ {
+ FT_BitmapGlyph p_glyph = p_string->pp_glyphs[ i ];
+#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ ( x + y * p_glyph->bitmap.width ) ] ]
+#define pixel p_in[ ( (p_string->p_glyph_pos[ i ].y>>1) + pen_y + (y>>1) - ( p_glyph->top >> 1 ) ) * i_pitch + ( x >> 1 ) + pen_x + ( p_string->p_glyph_pos[ i ].x >> 1 ) + ( p_glyph->left >>1) ]
+ for( y = 0; y < p_glyph->bitmap.rows; y+=2 )
+ {
+ for( x = 0; x < p_glyph->bitmap.width; x+=2 )
+ {
+ pixel = ( ( pixel * ( 0xFF - alpha ) ) >> 8 ) +
+ ( 0x80 * alpha >> 8 );
+#undef alpha
+#undef pixel
+ }
+ }
+ }
+ }
+ }
}
}
+/**
+ * Draw a string on a YUY2 picture
+ */
+static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
+ const subpicture_t *p_subpic )
+{
+ subpicture_sys_t *p_string = p_subpic->p_sys;
+ int x, y, pen_x, pen_y;
+ unsigned int i;
+
+ uint8_t *p_in;
+ int i_pitch = p_pic->p[0].i_pitch;
+
+ p_in = p_pic->p->p_pixels;
+
+ if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
+ {
+ pen_y = p_pic->p->i_lines - p_string->i_height -
+ p_string->i_y_margin;
+ }
+ else
+ {
+ pen_y = p_string->i_y_margin;
+ }
+ pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.height / 100;
+ if ( p_string->i_flags & OSD_ALIGN_RIGHT )
+ {
+ pen_x = i_pitch - p_string->i_width - p_string->i_x_margin;
+ }
+ else
+ {
+ pen_x = p_string->i_x_margin;
+ }
+
+ /* TODO : set U & V bytes */
+ for( i = 0; p_string->pp_glyphs[i] != NULL; i++ )
+ {
+ if( p_string->pp_glyphs[i] )
+ {
+ FT_BitmapGlyph p_glyph = p_string->pp_glyphs[ i ];
+#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
+#define pixel p_in[ ( p_string->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pitch + 2 * ( x + pen_x + p_string->p_glyph_pos[ i ].x + p_glyph->left ) ]
+ for( y = 0; y < p_glyph->bitmap.rows; y++ )
+ {
+ for( x = 0; x < p_glyph->bitmap.width; x++ )
+ {
+ pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
+ ( 255 * alpha >> 8 );
+#undef alpha
+#undef pixel
+ }
+ }
+ }
+ }
+}
+
+
/**
* This function receives a string and creates a subpicture for it. It
* also calculates the size needed for this string, and renders the
* the vout method by this module
*/
static int AddText ( vout_thread_t *p_vout, byte_t *psz_string,
- text_style_t *p_style, int i_flags, int i_hmargin,
- int i_vmargin, mtime_t i_start, mtime_t i_stop )
+ text_style_t *p_style, int i_flags, int i_hmargin,
+ int i_vmargin, mtime_t i_start, mtime_t i_stop )
{
subpicture_sys_t *p_string;
int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous, i_char;
FT_BBox line;
FT_BBox glyph_size;
FT_Vector result;
+ FT_Glyph tmp_glyph;
result.x = 0;
result.y = 0;
p_subpic = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
if ( p_subpic == NULL )
{
- return VLC_EGENERIC;
+ return VLC_EGENERIC;
}
p_subpic->pf_render = Render;
p_subpic->pf_destroy = FreeString;
p_subpic->i_stop = i_stop;
if( i_stop == 0 )
{
- p_subpic->b_ephemer = VLC_TRUE;
+ p_subpic->b_ephemer = VLC_TRUE;
}
else
{
- p_subpic->b_ephemer = VLC_FALSE;
+ p_subpic->b_ephemer = VLC_FALSE;
}
/* Create and initialize private data for the subpicture */
p_string = malloc( sizeof(subpicture_sys_t) );
if ( p_string == NULL )
{
- vout_DestroySubPicture( p_vout, p_subpic );
- return VLC_ENOMEM;
+ vout_DestroySubPicture( p_vout, p_subpic );
+ return VLC_ENOMEM;
}
p_subpic->p_sys = p_string;
p_string->i_flags = i_flags;
i_char = GetUnicodeCharFromUTF8( &psz_string );
#define face p_vout->p_text_renderer_data->p_face
#define glyph face->glyph
+ if ( i_char == 13 ) /* ignore CR chars wherever they may be */
+ {
+ continue;
+ }
if ( i_char == '\n' )
{
i_pen_x = 0;
result.x = __MAX( result.x, line.xMax );
- result.y += line.yMax - line.yMin;
+ result.y += face->size->metrics.height / 100;
line.xMin = 0;
line.xMax = 0;
line.yMin = 0;
line.yMax = 0;
- i_pen_y = result.y + 1;
+ i_pen_y += face->size->metrics.height / 100;
continue;
}
i_glyph_index = FT_Get_Char_Index( face, i_char );
FT_Get_Kerning( face, i_previous, i_glyph_index,
ft_kerning_default, &delta );
i_pen_x += delta.x >> 6;
-
+
}
p_string->p_glyph_pos[ i ].x = i_pen_x;
p_string->p_glyph_pos[ i ].y = i_pen_y;
msg_Err( p_vout, "FT_Load_Glyph returned %d", i_error );
return VLC_EGENERIC;
}
- i_error = FT_Get_Glyph( glyph, &p_string->pp_glyphs[ i ] );
+ i_error = FT_Get_Glyph( glyph, &tmp_glyph );
if ( i_error )
{
msg_Err( p_vout, "FT_Get_Glyph returned %d", i_error );
return VLC_EGENERIC;
}
- FT_Glyph_Get_CBox( p_string->pp_glyphs[i],
- ft_glyph_bbox_pixels, &glyph_size );
+ FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
+ i_error = FT_Glyph_To_Bitmap( &tmp_glyph,
+ ft_render_mode_normal,
+ NULL,
+ 1 );
+ if ( i_error ) continue;
+ p_string->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
+
/* Do rest */
line.xMax = p_string->p_glyph_pos[i].x + glyph_size.xMax - glyph_size.xMin;
line.yMax = __MAX( line.yMax, glyph_size.yMax );
line.yMin = __MIN( line.yMin, glyph_size.yMin );
-
+
i_previous = i_glyph_index;
i_pen_x += glyph->advance.x >> 6;
- i++;
+ i++;
}
p_string->pp_glyphs[i] = NULL;
result.x = __MAX( result.x, line.xMax );
result.y += line.yMax - line.yMin;
p_string->i_height = result.y;
p_string->i_width = result.x;
- msg_Dbg( p_vout, "string height is %d, width is %d", p_string->i_height, p_string->i_width );
+ msg_Dbg( p_vout, "string height is %d, width is %d",
+ p_string->i_height, p_string->i_width );
msg_Dbg( p_vout, "adding string \"%s\" at (%d,%d) start_date "I64Fd
" end_date" I64Fd, p_string->psz_text, p_string->i_x_margin,
p_string->i_y_margin, i_start, i_stop );
subpicture_sys_t *p_string = p_subpic->p_sys;
for ( i = 0; p_string->pp_glyphs[ i ] != NULL; i++ )
{
- FT_Done_Glyph( p_string->pp_glyphs[ i ] );
+ FT_Done_Glyph( (FT_Glyph)p_string->pp_glyphs[ i ] );
}
free( p_string->psz_text );
free( p_string->p_glyph_pos );