2011-02-21 02:02:29 +02:00
/*
* Copyright ( c ) 2011 Stefano Sabatini
* Copyright ( c ) 2010 S . N . Hemanth Meenakshisundaram
* Copyright ( c ) 2003 Gustavo Sverzut Barbieri < gsbarbieri @ yahoo . com . br >
*
2011-05-31 15:00:12 +03:00
* This file is part of Libav .
2011-02-21 02:02:29 +02:00
*
2011-05-31 15:00:12 +03:00
* Libav is free software ; you can redistribute it and / or
2011-02-21 02:02:29 +02:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
2011-05-31 15:00:12 +03:00
* Libav is distributed in the hope that it will be useful ,
2011-02-21 02:02:29 +02:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2011-05-31 15:00:12 +03:00
* License along with Libav ; if not , write to the Free Software
2011-02-21 02:02:29 +02:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
/**
* @ file
2011-10-30 19:56:57 +03:00
* drawtext filter , based on the original vhook / drawtext . c
2011-02-21 02:02:29 +02:00
* filter by Gustavo Sverzut Barbieri
*/
2014-04-12 17:02:50 +03:00
# include "config.h"
# include <sys/types.h>
2011-02-21 02:02:29 +02:00
# include <sys/time.h>
2014-04-12 17:02:50 +03:00
# include <sys/stat.h>
2011-02-21 02:02:29 +02:00
# include <time.h>
2014-04-12 17:02:50 +03:00
# include <unistd.h>
# if CONFIG_LIBFONTCONFIG
# include <fontconfig/fontconfig.h>
# endif
2011-02-21 02:02:29 +02:00
# include "libavutil/colorspace.h"
2012-08-16 10:22:31 +03:00
# include "libavutil/common.h"
2011-02-21 02:02:29 +02:00
# include "libavutil/file.h"
2011-12-01 13:43:11 +03:00
# include "libavutil/eval.h"
2011-02-21 02:02:29 +02:00
# include "libavutil/opt.h"
2011-12-01 13:43:11 +03:00
# include "libavutil/mathematics.h"
2011-12-05 02:56:21 +03:00
# include "libavutil/random_seed.h"
2011-02-21 02:02:29 +02:00
# include "libavutil/parseutils.h"
# include "libavutil/pixdesc.h"
2014-10-24 10:59:34 +03:00
# include "libavutil/time_internal.h"
2011-02-21 02:02:29 +02:00
# include "libavutil/tree.h"
2011-12-05 02:56:21 +03:00
# include "libavutil/lfg.h"
2011-02-21 02:02:29 +02:00
# include "avfilter.h"
# include "drawutils.h"
2012-05-30 11:12:55 +03:00
# include "formats.h"
2012-06-12 21:12:42 +03:00
# include "internal.h"
2012-05-19 11:37:56 +03:00
# include "video.h"
2011-02-21 02:02:29 +02:00
# include <ft2build.h>
# include FT_FREETYPE_H
# include FT_GLYPH_H
2012-02-20 11:42:33 +03:00
static const char * const var_names [ ] = {
2011-12-01 13:43:11 +03:00
" E " ,
" PHI " ,
" PI " ,
" main_w " , " W " , ///< width of the main video
" main_h " , " H " , ///< height of the main video
" text_w " , " w " , ///< width of the overlay text
" text_h " , " h " , ///< height of the overlay text
" x " ,
" y " ,
" n " , ///< number of processed frames
" t " , ///< timestamp expressed in seconds
NULL
} ;
2012-02-20 11:42:33 +03:00
static const char * const fun2_names [ ] = {
" rand "
2011-12-05 02:56:21 +03:00
} ;
static double drand ( void * opaque , double min , double max )
{
2011-12-07 22:30:55 +03:00
return min + ( max - min ) / UINT_MAX * av_lfg_get ( opaque ) ;
2011-12-05 02:56:21 +03:00
}
typedef double ( * eval_func2 ) ( void * , double a , double b ) ;
static const eval_func2 fun2 [ ] = {
drand ,
NULL
} ;
2011-12-01 13:43:11 +03:00
enum var_name {
VAR_E ,
VAR_PHI ,
VAR_PI ,
VAR_MAIN_W , VAR_MW ,
VAR_MAIN_H , VAR_MH ,
VAR_TEXT_W , VAR_TW ,
VAR_TEXT_H , VAR_TH ,
VAR_X ,
VAR_Y ,
VAR_N ,
VAR_T ,
VAR_VARS_NB
} ;
2014-04-11 12:54:15 +03:00
typedef struct DrawTextContext {
2011-02-21 02:02:29 +02:00
const AVClass * class ;
2014-04-12 17:02:50 +03:00
# if CONFIG_LIBFONTCONFIG
uint8_t * font ; ///< font to be used
# endif
2011-02-21 02:02:29 +02:00
uint8_t * fontfile ; ///< font to be used
uint8_t * text ; ///< text to be drawn
2011-05-16 21:11:50 +03:00
uint8_t * expanded_text ; ///< used to contain the strftime()-expanded text
size_t expanded_text_size ; ///< size in bytes of the expanded_text buffer
2011-02-21 02:02:29 +02:00
int ft_load_flags ; ///< flags used for loading fonts, see FT_LOAD_*
FT_Vector * positions ; ///< positions for each element in the text
2011-05-16 21:11:50 +03:00
size_t nb_positions ; ///< number of elements of positions array
2011-02-21 02:02:29 +02:00
char * textfile ; ///< file with text to be drawn
2011-12-01 13:14:54 +03:00
int x , y ; ///< position to start drawing text
int w , h ; ///< dimension of the text block
2011-02-22 02:41:52 +02:00
int shadowx , shadowy ;
2011-02-21 02:02:29 +02:00
unsigned int fontsize ; ///< font size to use
char * fontcolor_string ; ///< font color as string
char * boxcolor_string ; ///< box color as string
2011-02-22 02:41:52 +02:00
char * shadowcolor_string ; ///< shadow color as string
2011-02-21 02:02:29 +02:00
uint8_t fontcolor [ 4 ] ; ///< foreground color
uint8_t boxcolor [ 4 ] ; ///< background color
2011-02-22 02:41:52 +02:00
uint8_t shadowcolor [ 4 ] ; ///< shadow color
2011-02-21 02:02:29 +02:00
uint8_t fontcolor_rgba [ 4 ] ; ///< foreground color in RGBA
uint8_t boxcolor_rgba [ 4 ] ; ///< background color in RGBA
2011-02-22 02:41:52 +02:00
uint8_t shadowcolor_rgba [ 4 ] ; ///< shadow color in RGBA
2011-02-21 02:02:29 +02:00
short int draw_box ; ///< draw box around text - true or false
int use_kerning ; ///< font kerning is used - true/false
int tabsize ; ///< tab size
2012-02-05 15:41:01 +03:00
int fix_bounds ; ///< do we let it go out of frame bounds - t/f
2011-02-21 02:02:29 +02:00
FT_Library library ; ///< freetype font library handle
FT_Face face ; ///< freetype font face handle
struct AVTreeNode * glyphs ; ///< rendered glyphs, stored using the UTF-32 char code
int hsub , vsub ; ///< chroma subsampling values
int is_packed_rgb ;
int pixel_step [ 4 ] ; ///< distance in bytes between the component of each pixel
uint8_t rgba_map [ 4 ] ; ///< map RGBA offsets to the positions in the packed RGBA format
uint8_t * box_line [ 4 ] ; ///< line used for filling the box background
2011-12-01 13:43:11 +03:00
char * x_expr , * y_expr ;
AVExpr * x_pexpr , * y_pexpr ; ///< parsed expressions for x and y
double var_values [ VAR_VARS_NB ] ;
2011-12-04 23:13:56 +03:00
char * d_expr ;
AVExpr * d_pexpr ;
2011-12-01 13:43:11 +03:00
int draw ; ///< set to zero to prevent drawing
2015-04-11 19:04:25 +02:00
char * a_expr ;
AVExpr * a_pexpr ;
int alpha ;
2011-12-05 02:56:21 +03:00
AVLFG prng ; ///< random
2011-02-21 02:02:29 +02:00
} DrawTextContext ;
# define OFFSET(x) offsetof(DrawTextContext, x)
2013-02-25 23:21:29 +03:00
# define FLAGS AV_OPT_FLAG_VIDEO_PARAM
2011-02-21 02:02:29 +02:00
static const AVOption drawtext_options [ ] = {
2014-04-12 17:02:50 +03:00
# if CONFIG_LIBFONTCONFIG
{ " font " , " Font name " , OFFSET ( font ) , AV_OPT_TYPE_STRING , { . str = " Sans " } , . flags = FLAGS } ,
# endif
2013-02-25 23:21:29 +03:00
{ " fontfile " , NULL , OFFSET ( fontfile ) , AV_OPT_TYPE_STRING , . flags = FLAGS } ,
{ " text " , NULL , OFFSET ( text ) , AV_OPT_TYPE_STRING , . flags = FLAGS } ,
{ " textfile " , NULL , OFFSET ( textfile ) , AV_OPT_TYPE_STRING , . flags = FLAGS } ,
{ " fontcolor " , NULL , OFFSET ( fontcolor_string ) , AV_OPT_TYPE_STRING , { . str = " black " } , . flags = FLAGS } ,
{ " boxcolor " , NULL , OFFSET ( boxcolor_string ) , AV_OPT_TYPE_STRING , { . str = " white " } , . flags = FLAGS } ,
{ " shadowcolor " , NULL , OFFSET ( shadowcolor_string ) , AV_OPT_TYPE_STRING , { . str = " black " } , . flags = FLAGS } ,
{ " box " , NULL , OFFSET ( draw_box ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , 1 , FLAGS } ,
2014-12-02 16:09:30 +02:00
{ " fontsize " , NULL , OFFSET ( fontsize ) , AV_OPT_TYPE_INT , { . i64 = 16 } , 1 , 1024 , FLAGS } ,
2013-02-25 23:21:29 +03:00
{ " x " , NULL , OFFSET ( x_expr ) , AV_OPT_TYPE_STRING , { . str = " 0 " } , . flags = FLAGS } ,
{ " y " , NULL , OFFSET ( y_expr ) , AV_OPT_TYPE_STRING , { . str = " 0 " } , . flags = FLAGS } ,
{ " shadowx " , NULL , OFFSET ( shadowx ) , AV_OPT_TYPE_INT , { . i64 = 0 } , INT_MIN , INT_MAX , FLAGS } ,
{ " shadowy " , NULL , OFFSET ( shadowy ) , AV_OPT_TYPE_INT , { . i64 = 0 } , INT_MIN , INT_MAX , FLAGS } ,
{ " tabsize " , NULL , OFFSET ( tabsize ) , AV_OPT_TYPE_INT , { . i64 = 4 } , 0 , INT_MAX , FLAGS } ,
{ " draw " , " if false do not draw " , OFFSET ( d_expr ) , AV_OPT_TYPE_STRING , { . str = " 1 " } , . flags = FLAGS } ,
2015-04-11 19:04:25 +02:00
{ " alpha " , " apply alpha while rendering " , OFFSET ( a_expr ) , AV_OPT_TYPE_STRING , { . str = " 1 " } , . flags = FLAGS } ,
2013-02-25 23:21:29 +03:00
{ " fix_bounds " , " if true, check and fix text coords to avoid clipping " ,
OFFSET ( fix_bounds ) , AV_OPT_TYPE_INT , { . i64 = 1 } , 0 , 1 , FLAGS } ,
/* FT_LOAD_* flags */
{ " ft_load_flags " , " set font loading flags for libfreetype " , OFFSET ( ft_load_flags ) , AV_OPT_TYPE_FLAGS , { . i64 = FT_LOAD_DEFAULT | FT_LOAD_RENDER } , 0 , INT_MAX , FLAGS , " ft_load_flags " } ,
{ " default " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_DEFAULT } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " no_scale " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_NO_SCALE } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " no_hinting " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_NO_HINTING } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " render " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_RENDER } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " no_bitmap " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_NO_BITMAP } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " vertical_layout " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_VERTICAL_LAYOUT } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " force_autohint " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_FORCE_AUTOHINT } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " crop_bitmap " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_CROP_BITMAP } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " pedantic " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_PEDANTIC } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " ignore_global_advance_width " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " no_recurse " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_NO_RECURSE } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " ignore_transform " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_IGNORE_TRANSFORM } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " monochrome " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_MONOCHROME } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " linear_design " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_LINEAR_DESIGN } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ " no_autohint " , NULL , 0 , AV_OPT_TYPE_CONST , { . i64 = FT_LOAD_NO_AUTOHINT } , . flags = FLAGS , . unit = " ft_load_flags " } ,
{ NULL } ,
2011-02-21 02:02:29 +02:00
} ;
static const char * drawtext_get_name ( void * ctx )
{
return " drawtext " ;
}
static const AVClass drawtext_class = {
" DrawTextContext " ,
drawtext_get_name ,
drawtext_options
} ;
# undef __FTERRORS_H__
# define FT_ERROR_START_LIST {
# define FT_ERRORDEF(e, v, s) { (e), (s) },
# define FT_ERROR_END_LIST { 0, NULL } };
struct ft_error
{
int err ;
const char * err_msg ;
} static ft_errors [ ] =
# include FT_ERRORS_H
# define FT_ERRMSG(e) ft_errors[e].err_msg
2014-04-11 12:54:15 +03:00
typedef struct Glyph {
2011-02-21 02:02:29 +02:00
FT_Glyph * glyph ;
uint32_t code ;
FT_Bitmap bitmap ; ///< array holding bitmaps of font
FT_BBox bbox ;
int advance ;
int bitmap_left ;
int bitmap_top ;
} Glyph ;
static int glyph_cmp ( void * key , const void * b )
{
const Glyph * a = key , * bb = b ;
int64_t diff = ( int64_t ) a - > code - ( int64_t ) bb - > code ;
return diff > 0 ? 1 : diff < 0 ? - 1 : 0 ;
}
/**
* Load glyphs corresponding to the UTF - 32 codepoint code .
*/
static int load_glyph ( AVFilterContext * ctx , Glyph * * glyph_ptr , uint32_t code )
{
2013-03-18 22:44:36 +03:00
DrawTextContext * s = ctx - > priv ;
2011-02-21 02:02:29 +02:00
Glyph * glyph ;
struct AVTreeNode * node = NULL ;
int ret ;
2013-03-18 22:44:36 +03:00
/* load glyph into s->face->glyph */
if ( FT_Load_Char ( s - > face , code , s - > ft_load_flags ) )
2011-02-21 02:02:29 +02:00
return AVERROR ( EINVAL ) ;
/* save glyph */
if ( ! ( glyph = av_mallocz ( sizeof ( * glyph ) ) ) | |
! ( glyph - > glyph = av_mallocz ( sizeof ( * glyph - > glyph ) ) ) ) {
ret = AVERROR ( ENOMEM ) ;
goto error ;
}
glyph - > code = code ;
2013-03-18 22:44:36 +03:00
if ( FT_Get_Glyph ( s - > face - > glyph , glyph - > glyph ) ) {
2011-02-21 02:02:29 +02:00
ret = AVERROR ( EINVAL ) ;
goto error ;
}
2013-03-18 22:44:36 +03:00
glyph - > bitmap = s - > face - > glyph - > bitmap ;
glyph - > bitmap_left = s - > face - > glyph - > bitmap_left ;
glyph - > bitmap_top = s - > face - > glyph - > bitmap_top ;
glyph - > advance = s - > face - > glyph - > advance . x > > 6 ;
2011-02-21 02:02:29 +02:00
/* measure text height to calculate text_height (or the maximum text height) */
FT_Glyph_Get_CBox ( * glyph - > glyph , ft_glyph_bbox_pixels , & glyph - > bbox ) ;
/* cache the newly created glyph */
2012-11-02 12:56:30 +03:00
if ( ! ( node = av_tree_node_alloc ( ) ) ) {
2011-02-21 02:02:29 +02:00
ret = AVERROR ( ENOMEM ) ;
goto error ;
}
2013-03-18 22:44:36 +03:00
av_tree_insert ( & s - > glyphs , glyph , glyph_cmp , & node ) ;
2011-02-21 02:02:29 +02:00
if ( glyph_ptr )
* glyph_ptr = glyph ;
return 0 ;
error :
if ( glyph )
av_freep ( & glyph - > glyph ) ;
av_freep ( & glyph ) ;
av_freep ( & node ) ;
return ret ;
}
2014-04-12 17:02:50 +03:00
static int parse_font ( AVFilterContext * ctx )
2011-02-21 02:02:29 +02:00
{
2013-03-18 22:44:36 +03:00
DrawTextContext * s = ctx - > priv ;
2014-04-12 17:02:50 +03:00
# if !CONFIG_LIBFONTCONFIG
2013-03-18 22:44:36 +03:00
if ( ! s - > fontfile ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR , " No font filename provided \n " ) ;
return AVERROR ( EINVAL ) ;
}
2014-04-12 17:02:50 +03:00
return 0 ;
# else
FcPattern * pat , * best ;
FcResult result = FcResultMatch ;
FcBool fc_bool ;
FcChar8 * fc_string ;
int err = AVERROR ( ENOENT ) ;
if ( s - > fontfile )
return 0 ;
if ( ! FcInit ( ) )
return AVERROR_UNKNOWN ;
if ( ! ( pat = FcPatternCreate ( ) ) )
return AVERROR ( ENOMEM ) ;
FcPatternAddString ( pat , FC_FAMILY , s - > font ) ;
FcPatternAddBool ( pat , FC_OUTLINE , FcTrue ) ;
FcPatternAddDouble ( pat , FC_SIZE , ( double ) s - > fontsize ) ;
FcDefaultSubstitute ( pat ) ;
if ( ! FcConfigSubstitute ( NULL , pat , FcMatchPattern ) ) {
FcPatternDestroy ( pat ) ;
return AVERROR ( ENOMEM ) ;
}
best = FcFontMatch ( NULL , pat , & result ) ;
FcPatternDestroy ( pat ) ;
if ( ! best | | result = = FcResultNoMatch ) {
av_log ( ctx , AV_LOG_ERROR ,
" Cannot find a valid font for the family %s \n " ,
s - > font ) ;
goto fail ;
}
if ( FcPatternGetBool ( best , FC_OUTLINE , 0 , & fc_bool ) ! = FcResultMatch | |
! fc_bool ) {
av_log ( ctx , AV_LOG_ERROR , " Outline not available for %s \n " ,
s - > font ) ;
goto fail ;
}
if ( FcPatternGetString ( best , FC_FAMILY , 0 , & fc_string ) ! = FcResultMatch ) {
av_log ( ctx , AV_LOG_ERROR , " No matches for %s \n " ,
s - > font ) ;
goto fail ;
}
if ( FcPatternGetString ( best , FC_FILE , 0 , & fc_string ) ! = FcResultMatch ) {
av_log ( ctx , AV_LOG_ERROR , " No file path for %s \n " ,
s - > font ) ;
goto fail ;
}
s - > fontfile = av_strdup ( fc_string ) ;
if ( ! s - > fontfile )
err = AVERROR ( ENOMEM ) ;
else
err = 0 ;
fail :
FcPatternDestroy ( best ) ;
return err ;
# endif
}
static av_cold int init ( AVFilterContext * ctx )
{
int err ;
DrawTextContext * s = ctx - > priv ;
Glyph * glyph ;
if ( ( err = parse_font ( ctx ) ) < 0 )
return err ;
2013-03-18 22:44:36 +03:00
if ( s - > textfile ) {
2011-02-21 02:02:29 +02:00
uint8_t * textbuf ;
size_t textbuf_size ;
2013-03-18 22:44:36 +03:00
if ( s - > text ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR ,
" Both text and text file provided. Please provide only one \n " ) ;
return AVERROR ( EINVAL ) ;
}
2013-03-18 22:44:36 +03:00
if ( ( err = av_file_map ( s - > textfile , & textbuf , & textbuf_size , 0 , ctx ) ) < 0 ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR ,
" The text file '%s' could not be read or is empty \n " ,
2013-03-18 22:44:36 +03:00
s - > textfile ) ;
2011-02-21 02:02:29 +02:00
return err ;
}
2014-10-17 12:07:10 +03:00
if ( textbuf_size > SIZE_MAX - 1 | |
! ( s - > text = av_malloc ( textbuf_size + 1 ) ) ) {
av_file_unmap ( textbuf , textbuf_size ) ;
2011-02-21 02:02:29 +02:00
return AVERROR ( ENOMEM ) ;
2014-10-17 12:07:10 +03:00
}
2013-03-18 22:44:36 +03:00
memcpy ( s - > text , textbuf , textbuf_size ) ;
s - > text [ textbuf_size ] = 0 ;
2011-02-21 02:02:29 +02:00
av_file_unmap ( textbuf , textbuf_size ) ;
}
2013-03-18 22:44:36 +03:00
if ( ! s - > text ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR ,
" Either text or a valid file must be provided \n " ) ;
return AVERROR ( EINVAL ) ;
}
2013-03-18 22:44:36 +03:00
if ( ( err = av_parse_color ( s - > fontcolor_rgba , s - > fontcolor_string , - 1 , ctx ) ) ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR ,
2013-03-18 22:44:36 +03:00
" Invalid font color '%s' \n " , s - > fontcolor_string ) ;
2011-02-21 02:02:29 +02:00
return err ;
}
2013-03-18 22:44:36 +03:00
if ( ( err = av_parse_color ( s - > boxcolor_rgba , s - > boxcolor_string , - 1 , ctx ) ) ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR ,
2013-03-18 22:44:36 +03:00
" Invalid box color '%s' \n " , s - > boxcolor_string ) ;
2011-02-21 02:02:29 +02:00
return err ;
}
2013-03-18 22:44:36 +03:00
if ( ( err = av_parse_color ( s - > shadowcolor_rgba , s - > shadowcolor_string , - 1 , ctx ) ) ) {
2011-02-22 02:41:52 +02:00
av_log ( ctx , AV_LOG_ERROR ,
2013-03-18 22:44:36 +03:00
" Invalid shadow color '%s' \n " , s - > shadowcolor_string ) ;
2011-02-22 02:41:52 +02:00
return err ;
}
2013-03-18 22:44:36 +03:00
if ( ( err = FT_Init_FreeType ( & ( s - > library ) ) ) ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR ,
" Could not load FreeType: %s \n " , FT_ERRMSG ( err ) ) ;
return AVERROR ( EINVAL ) ;
}
/* load the face, and set up the encoding, which is by default UTF-8 */
2013-03-18 22:44:36 +03:00
if ( ( err = FT_New_Face ( s - > library , s - > fontfile , 0 , & s - > face ) ) ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR , " Could not load fontface from file '%s': %s \n " ,
2013-03-18 22:44:36 +03:00
s - > fontfile , FT_ERRMSG ( err ) ) ;
2011-02-21 02:02:29 +02:00
return AVERROR ( EINVAL ) ;
}
2013-03-18 22:44:36 +03:00
if ( ( err = FT_Set_Pixel_Sizes ( s - > face , 0 , s - > fontsize ) ) ) {
2011-02-21 02:02:29 +02:00
av_log ( ctx , AV_LOG_ERROR , " Could not set font size to %d pixels: %s \n " ,
2013-03-18 22:44:36 +03:00
s - > fontsize , FT_ERRMSG ( err ) ) ;
2011-02-21 02:02:29 +02:00
return AVERROR ( EINVAL ) ;
}
2013-03-18 22:44:36 +03:00
s - > use_kerning = FT_HAS_KERNING ( s - > face ) ;
2011-02-21 02:02:29 +02:00
/* load the fallback glyph with code 0 */
load_glyph ( ctx , NULL , 0 ) ;
/* set the tabsize in pixels */
if ( ( err = load_glyph ( ctx , & glyph , ' ' ) < 0 ) ) {
av_log ( ctx , AV_LOG_ERROR , " Could not set tabsize. \n " ) ;
return err ;
}
2013-03-18 22:44:36 +03:00
s - > tabsize * = glyph - > advance ;
2011-02-21 02:02:29 +02:00
return 0 ;
}
static int query_formats ( AVFilterContext * ctx )
{
2012-10-06 13:10:34 +03:00
static const enum AVPixelFormat pix_fmts [ ] = {
AV_PIX_FMT_ARGB , AV_PIX_FMT_RGBA ,
AV_PIX_FMT_ABGR , AV_PIX_FMT_BGRA ,
AV_PIX_FMT_RGB24 , AV_PIX_FMT_BGR24 ,
AV_PIX_FMT_YUV420P , AV_PIX_FMT_YUV444P ,
AV_PIX_FMT_YUV422P , AV_PIX_FMT_YUV411P ,
AV_PIX_FMT_YUV410P , AV_PIX_FMT_YUV440P ,
AV_PIX_FMT_NONE
2011-02-21 02:02:29 +02:00
} ;
2012-05-30 11:12:55 +03:00
ff_set_common_formats ( ctx , ff_make_format_list ( pix_fmts ) ) ;
2011-02-21 02:02:29 +02:00
return 0 ;
}
static int glyph_enu_free ( void * opaque , void * elem )
{
av_free ( elem ) ;
return 0 ;
}
static av_cold void uninit ( AVFilterContext * ctx )
{
2013-03-18 22:44:36 +03:00
DrawTextContext * s = ctx - > priv ;
2011-02-21 02:02:29 +02:00
int i ;
2013-03-19 00:13:35 +03:00
av_expr_free ( s - > x_pexpr ) ;
av_expr_free ( s - > y_pexpr ) ;
av_expr_free ( s - > d_pexpr ) ;
s - > x_pexpr = s - > y_pexpr = s - > d_pexpr = NULL ;
2013-03-18 22:44:36 +03:00
av_freep ( & s - > expanded_text ) ;
av_freep ( & s - > positions ) ;
av_tree_enumerate ( s - > glyphs , NULL , NULL , glyph_enu_free ) ;
av_tree_destroy ( s - > glyphs ) ;
s - > glyphs = 0 ;
FT_Done_Face ( s - > face ) ;
FT_Done_FreeType ( s - > library ) ;
2011-02-21 02:02:29 +02:00
for ( i = 0 ; i < 4 ; i + + ) {
2013-03-18 22:44:36 +03:00
av_freep ( & s - > box_line [ i ] ) ;
s - > pixel_step [ i ] = 0 ;
2011-02-21 02:02:29 +02:00
}
}
2011-12-01 13:27:19 +03:00
static inline int is_newline ( uint32_t c )
{
2011-12-30 00:23:16 +03:00
return c = = ' \n ' | | c = = ' \r ' | | c = = ' \f ' | | c = = ' \v ' ;
2011-12-01 13:27:19 +03:00
}
2015-08-01 11:22:11 +02:00
static int expand_strftime ( DrawTextContext * s )
2011-12-01 13:27:19 +03:00
{
struct tm ltime ;
2015-08-01 11:22:11 +02:00
time_t now = time ( 0 ) ;
2013-03-18 22:44:36 +03:00
uint8_t * buf = s - > expanded_text ;
int buf_size = s - > expanded_text_size ;
2011-12-01 13:27:19 +03:00
if ( ! buf )
2015-08-01 11:22:11 +02:00
buf_size = 2 * strlen ( s - > text ) + 1 ;
2011-12-01 13:27:19 +03:00
localtime_r ( & now , & ltime ) ;
while ( ( buf = av_realloc ( buf , buf_size ) ) ) {
* buf = 1 ;
2013-03-18 22:44:36 +03:00
if ( strftime ( buf , buf_size , s - > text , & ltime ) ! = 0 | | * buf = = 0 )
2011-12-01 13:27:19 +03:00
break ;
buf_size * = 2 ;
}
if ( ! buf )
return AVERROR ( ENOMEM ) ;
2015-08-01 11:22:11 +02:00
s - > expanded_text = buf ;
2013-03-18 22:44:36 +03:00
s - > expanded_text_size = buf_size ;
2011-12-01 13:27:19 +03:00
2015-08-01 11:22:11 +02:00
return 0 ;
}
static int dtext_prepare_text ( AVFilterContext * ctx )
{
DrawTextContext * s = ctx - > priv ;
uint32_t code = 0 , prev_code = 0 ;
int x = 0 , y = 0 , i = 0 , ret ;
int text_height , baseline ;
char * text ;
uint8_t * p ;
int str_w = 0 , len ;
int y_min = 32000 , y_max = - 32000 ;
FT_Vector delta ;
Glyph * glyph = NULL , * prev_glyph = NULL ;
Glyph dummy = { 0 } ;
int width = ctx - > inputs [ 0 ] - > w ;
int height = ctx - > inputs [ 0 ] - > h ;
ret = expand_strftime ( s ) ;
if ( ret < 0 )
return ret ;
text = s - > expanded_text ? s - > expanded_text : s - > text ;
2013-03-18 22:44:36 +03:00
if ( ( len = strlen ( text ) ) > s - > nb_positions ) {
FT_Vector * p = av_realloc ( s - > positions ,
len * sizeof ( * s - > positions ) ) ;
2011-12-01 13:27:19 +03:00
if ( ! p ) {
2013-03-18 22:44:36 +03:00
av_freep ( s - > positions ) ;
s - > nb_positions = 0 ;
2011-12-01 13:27:19 +03:00
return AVERROR ( ENOMEM ) ;
} else {
2013-03-18 22:44:36 +03:00
s - > positions = p ;
s - > nb_positions = len ;
2011-12-01 13:27:19 +03:00
}
}
/* load and cache glyphs */
for ( i = 0 , p = text ; * p ; i + + ) {
GET_UTF8 ( code , * p + + , continue ; ) ;
/* get glyph */
dummy . code = code ;
2013-03-18 22:44:36 +03:00
glyph = av_tree_find ( s - > glyphs , & dummy , glyph_cmp , NULL ) ;
2012-02-05 01:14:15 +03:00
if ( ! glyph ) {
2011-12-01 13:27:19 +03:00
ret = load_glyph ( ctx , & glyph , code ) ;
2012-02-05 01:14:15 +03:00
if ( ret )
return ret ;
}
2011-12-01 13:27:19 +03:00
y_min = FFMIN ( glyph - > bbox . yMin , y_min ) ;
y_max = FFMAX ( glyph - > bbox . yMax , y_max ) ;
}
text_height = y_max - y_min ;
baseline = y_max ;
/* compute and save position for each glyph */
glyph = NULL ;
for ( i = 0 , p = text ; * p ; i + + ) {
GET_UTF8 ( code , * p + + , continue ; ) ;
/* skip the \n in the sequence \r\n */
if ( prev_code = = ' \r ' & & code = = ' \n ' )
continue ;
prev_code = code ;
if ( is_newline ( code ) ) {
2013-03-18 22:44:36 +03:00
str_w = FFMAX ( str_w , x - s - > x ) ;
2011-12-01 13:27:19 +03:00
y + = text_height ;
x = 0 ;
continue ;
}
/* get glyph */
prev_glyph = glyph ;
dummy . code = code ;
2013-03-18 22:44:36 +03:00
glyph = av_tree_find ( s - > glyphs , & dummy , glyph_cmp , NULL ) ;
2011-12-01 13:27:19 +03:00
/* kerning */
2013-03-18 22:44:36 +03:00
if ( s - > use_kerning & & prev_glyph & & glyph - > code ) {
FT_Get_Kerning ( s - > face , prev_glyph - > code , glyph - > code ,
2011-12-01 13:27:19 +03:00
ft_kerning_default , & delta ) ;
x + = delta . x > > 6 ;
}
if ( x + glyph - > bbox . xMax > = width ) {
str_w = FFMAX ( str_w , x ) ;
y + = text_height ;
x = 0 ;
}
/* save position */
2013-03-18 22:44:36 +03:00
s - > positions [ i ] . x = x + glyph - > bitmap_left ;
s - > positions [ i ] . y = y - glyph - > bitmap_top + baseline ;
if ( code = = ' \t ' ) x = ( x / s - > tabsize + 1 ) * s - > tabsize ;
2011-12-01 13:27:19 +03:00
else x + = glyph - > advance ;
}
str_w = FFMIN ( width - 1 , FFMAX ( str_w , x ) ) ;
y = FFMIN ( y + text_height , height - 1 ) ;
2013-03-18 22:44:36 +03:00
s - > w = str_w ;
s - > var_values [ VAR_TEXT_W ] = s - > var_values [ VAR_TW ] = s - > w ;
s - > h = y ;
s - > var_values [ VAR_TEXT_H ] = s - > var_values [ VAR_TH ] = s - > h ;
2011-12-01 13:27:19 +03:00
return 0 ;
}
2011-02-21 02:02:29 +02:00
static int config_input ( AVFilterLink * inlink )
{
2011-12-01 13:27:19 +03:00
AVFilterContext * ctx = inlink - > dst ;
2013-03-18 22:44:36 +03:00
DrawTextContext * s = ctx - > priv ;
2012-10-06 14:29:37 +03:00
const AVPixFmtDescriptor * pix_desc = av_pix_fmt_desc_get ( inlink - > format ) ;
2011-02-21 02:02:29 +02:00
int ret ;
2013-03-18 22:44:36 +03:00
s - > hsub = pix_desc - > log2_chroma_w ;
s - > vsub = pix_desc - > log2_chroma_h ;
2011-02-21 02:02:29 +02:00
2013-03-18 22:44:36 +03:00
s - > var_values [ VAR_E ] = M_E ;
s - > var_values [ VAR_PHI ] = M_PHI ;
s - > var_values [ VAR_PI ] = M_PI ;
2011-12-01 13:43:11 +03:00
2013-03-18 22:44:36 +03:00
s - > var_values [ VAR_MAIN_W ] =
s - > var_values [ VAR_MW ] = ctx - > inputs [ 0 ] - > w ;
s - > var_values [ VAR_MAIN_H ] =
s - > var_values [ VAR_MH ] = ctx - > inputs [ 0 ] - > h ;
2011-12-01 13:43:11 +03:00
2013-03-18 22:44:36 +03:00
s - > var_values [ VAR_X ] = 0 ;
s - > var_values [ VAR_Y ] = 0 ;
s - > var_values [ VAR_T ] = NAN ;
2011-12-01 13:43:11 +03:00
2013-03-18 22:44:36 +03:00
av_lfg_init ( & s - > prng , av_get_random_seed ( ) ) ;
2011-12-01 13:43:11 +03:00
2013-03-19 00:13:35 +03:00
av_expr_free ( s - > x_pexpr ) ;
av_expr_free ( s - > y_pexpr ) ;
av_expr_free ( s - > d_pexpr ) ;
s - > x_pexpr = s - > y_pexpr = s - > d_pexpr = NULL ;
2013-03-18 22:44:36 +03:00
if ( ( ret = av_expr_parse ( & s - > x_pexpr , s - > x_expr , var_names ,
2011-12-05 02:56:21 +03:00
NULL , NULL , fun2_names , fun2 , 0 , ctx ) ) < 0 | |
2013-03-18 22:44:36 +03:00
( ret = av_expr_parse ( & s - > y_pexpr , s - > y_expr , var_names ,
2011-12-05 02:56:21 +03:00
NULL , NULL , fun2_names , fun2 , 0 , ctx ) ) < 0 | |
2013-03-18 22:44:36 +03:00
( ret = av_expr_parse ( & s - > d_pexpr , s - > d_expr , var_names ,
2015-04-11 19:04:25 +02:00
NULL , NULL , fun2_names , fun2 , 0 , ctx ) ) < 0 | |
( ret = av_expr_parse ( & s - > a_pexpr , s - > a_expr , var_names ,
2011-12-05 02:56:21 +03:00
NULL , NULL , fun2_names , fun2 , 0 , ctx ) ) < 0 )
2011-12-01 13:43:11 +03:00
return AVERROR ( EINVAL ) ;
2011-02-21 02:02:29 +02:00
if ( ( ret =
2013-03-18 22:44:36 +03:00
ff_fill_line_with_color ( s - > box_line , s - > pixel_step ,
inlink - > w , s - > boxcolor ,
inlink - > format , s - > boxcolor_rgba ,
& s - > is_packed_rgb , s - > rgba_map ) ) < 0 )
2011-02-21 02:02:29 +02:00
return ret ;
2013-03-18 22:44:36 +03:00
if ( ! s - > is_packed_rgb ) {
uint8_t * rgba = s - > fontcolor_rgba ;
s - > fontcolor [ 0 ] = RGB_TO_Y_CCIR ( rgba [ 0 ] , rgba [ 1 ] , rgba [ 2 ] ) ;
s - > fontcolor [ 1 ] = RGB_TO_U_CCIR ( rgba [ 0 ] , rgba [ 1 ] , rgba [ 2 ] , 0 ) ;
s - > fontcolor [ 2 ] = RGB_TO_V_CCIR ( rgba [ 0 ] , rgba [ 1 ] , rgba [ 2 ] , 0 ) ;
s - > fontcolor [ 3 ] = rgba [ 3 ] ;
rgba = s - > shadowcolor_rgba ;
s - > shadowcolor [ 0 ] = RGB_TO_Y_CCIR ( rgba [ 0 ] , rgba [ 1 ] , rgba [ 2 ] ) ;
s - > shadowcolor [ 1 ] = RGB_TO_U_CCIR ( rgba [ 0 ] , rgba [ 1 ] , rgba [ 2 ] , 0 ) ;
s - > shadowcolor [ 2 ] = RGB_TO_V_CCIR ( rgba [ 0 ] , rgba [ 1 ] , rgba [ 2 ] , 0 ) ;
s - > shadowcolor [ 3 ] = rgba [ 3 ] ;
2011-02-21 02:02:29 +02:00
}
2013-03-18 22:44:36 +03:00
s - > draw = 1 ;
2011-12-01 13:43:11 +03:00
return dtext_prepare_text ( ctx ) ;
2011-02-21 02:02:29 +02:00
}
# define GET_BITMAP_VAL(r, c) \
bitmap - > pixel_mode = = FT_PIXEL_MODE_MONO ? \
( bitmap - > buffer [ ( r ) * bitmap - > pitch + ( ( c ) > > 3 ) ] & ( 0x80 > > ( ( c ) & 7 ) ) ) * 255 : \
bitmap - > buffer [ ( r ) * bitmap - > pitch + ( c ) ]
2012-11-28 10:41:07 +03:00
# define SET_PIXEL_YUV(frame, yuva_color, val, x, y, hsub, vsub) { \
luma_pos = ( ( x ) ) + ( ( y ) ) * frame - > linesize [ 0 ] ; \
2015-04-11 19:04:25 +02:00
alpha = yuva_color [ 3 ] * alpha_mul * ( val ) * 129 / 255 ; \
2012-11-28 10:41:07 +03:00
frame - > data [ 0 ] [ luma_pos ] = ( alpha * yuva_color [ 0 ] + ( 255 * 255 * 129 - alpha ) * frame - > data [ 0 ] [ luma_pos ] ) > > 23 ; \
2011-02-22 01:54:48 +02:00
if ( ( ( x ) & ( ( 1 < < ( hsub ) ) - 1 ) ) = = 0 & & ( ( y ) & ( ( 1 < < ( vsub ) ) - 1 ) ) = = 0 ) { \
2012-11-28 10:41:07 +03:00
chroma_pos1 = ( ( x ) > > ( hsub ) ) + ( ( y ) > > ( vsub ) ) * frame - > linesize [ 1 ] ; \
chroma_pos2 = ( ( x ) > > ( hsub ) ) + ( ( y ) > > ( vsub ) ) * frame - > linesize [ 2 ] ; \
frame - > data [ 1 ] [ chroma_pos1 ] = ( alpha * yuva_color [ 1 ] + ( 255 * 255 * 129 - alpha ) * frame - > data [ 1 ] [ chroma_pos1 ] ) > > 23 ; \
frame - > data [ 2 ] [ chroma_pos2 ] = ( alpha * yuva_color [ 2 ] + ( 255 * 255 * 129 - alpha ) * frame - > data [ 2 ] [ chroma_pos2 ] ) > > 23 ; \
2011-02-22 01:54:48 +02:00
} \
2011-02-21 02:02:29 +02:00
}
2012-11-28 10:41:07 +03:00
static inline int draw_glyph_yuv ( AVFrame * frame , FT_Bitmap * bitmap , unsigned int x ,
2011-02-21 02:02:29 +02:00
unsigned int y , unsigned int width , unsigned int height ,
2015-04-11 19:04:25 +02:00
const uint8_t yuva_color [ 4 ] , int hsub , int vsub ,
int alpha_mul )
2011-02-21 02:02:29 +02:00
{
int r , c , alpha ;
unsigned int luma_pos , chroma_pos1 , chroma_pos2 ;
2011-07-07 02:31:05 +03:00
uint8_t src_val ;
2011-02-21 02:02:29 +02:00
for ( r = 0 ; r < bitmap - > rows & & r + y < height ; r + + ) {
for ( c = 0 ; c < bitmap - > width & & c + x < width ; c + + ) {
/* get intensity value in the glyph bitmap (source) */
src_val = GET_BITMAP_VAL ( r , c ) ;
if ( ! src_val )
continue ;
2012-11-28 10:41:07 +03:00
SET_PIXEL_YUV ( frame , yuva_color , src_val , c + x , y + r , hsub , vsub ) ;
2011-02-21 02:02:29 +02:00
}
}
return 0 ;
}
2012-11-28 10:41:07 +03:00
# define SET_PIXEL_RGB(frame, rgba_color, val, x, y, pixel_step, r_off, g_off, b_off, a_off) { \
p = frame - > data [ 0 ] + ( x ) * pixel_step + ( ( y ) * frame - > linesize [ 0 ] ) ; \
2015-04-11 19:04:25 +02:00
alpha = rgba_color [ 3 ] * alpha_mul * ( val ) * 129 / 255 ; \
2011-02-22 01:43:19 +02:00
* ( p + r_off ) = ( alpha * rgba_color [ 0 ] + ( 255 * 255 * 129 - alpha ) * * ( p + r_off ) ) > > 23 ; \
* ( p + g_off ) = ( alpha * rgba_color [ 1 ] + ( 255 * 255 * 129 - alpha ) * * ( p + g_off ) ) > > 23 ; \
* ( p + b_off ) = ( alpha * rgba_color [ 2 ] + ( 255 * 255 * 129 - alpha ) * * ( p + b_off ) ) > > 23 ; \
2011-02-21 02:02:29 +02:00
}
2012-11-28 10:41:07 +03:00
static inline int draw_glyph_rgb ( AVFrame * frame , FT_Bitmap * bitmap ,
2011-02-21 02:02:29 +02:00
unsigned int x , unsigned int y ,
unsigned int width , unsigned int height , int pixel_step ,
2015-04-11 19:04:25 +02:00
const uint8_t rgba_color [ 4 ] , const uint8_t rgba_map [ 4 ] ,
int alpha_mul )
2011-02-21 02:02:29 +02:00
{
int r , c , alpha ;
uint8_t * p ;
2011-07-07 02:31:05 +03:00
uint8_t src_val ;
2011-02-21 02:02:29 +02:00
for ( r = 0 ; r < bitmap - > rows & & r + y < height ; r + + ) {
for ( c = 0 ; c < bitmap - > width & & c + x < width ; c + + ) {
/* get intensity value in the glyph bitmap (source) */
src_val = GET_BITMAP_VAL ( r , c ) ;
if ( ! src_val )
continue ;
2012-11-28 10:41:07 +03:00
SET_PIXEL_RGB ( frame , rgba_color , src_val , c + x , y + r , pixel_step ,
2011-02-21 02:02:29 +02:00
rgba_map [ 0 ] , rgba_map [ 1 ] , rgba_map [ 2 ] , rgba_map [ 3 ] ) ;
}
}
return 0 ;
}
2012-11-28 10:41:07 +03:00
static inline void drawbox ( AVFrame * frame , unsigned int x , unsigned int y ,
2011-02-21 02:02:29 +02:00
unsigned int width , unsigned int height ,
uint8_t * line [ 4 ] , int pixel_step [ 4 ] , uint8_t color [ 4 ] ,
2015-04-11 19:04:25 +02:00
int hsub , int vsub , int is_rgba_packed , uint8_t rgba_map [ 4 ] ,
int alpha_mul )
2011-02-21 02:02:29 +02:00
{
int i , j , alpha ;
2015-04-11 19:04:25 +02:00
if ( color [ 3 ] ! = 0xFF | | alpha_mul ! = 0xFF ) {
2011-02-21 02:02:29 +02:00
if ( is_rgba_packed ) {
uint8_t * p ;
for ( j = 0 ; j < height ; j + + )
for ( i = 0 ; i < width ; i + + )
2012-11-28 10:41:07 +03:00
SET_PIXEL_RGB ( frame , color , 255 , i + x , y + j , pixel_step [ 0 ] ,
2011-02-21 02:02:29 +02:00
rgba_map [ 0 ] , rgba_map [ 1 ] , rgba_map [ 2 ] , rgba_map [ 3 ] ) ;
} else {
unsigned int luma_pos , chroma_pos1 , chroma_pos2 ;
for ( j = 0 ; j < height ; j + + )
for ( i = 0 ; i < width ; i + + )
2012-11-28 10:41:07 +03:00
SET_PIXEL_YUV ( frame , color , 255 , i + x , y + j , hsub , vsub ) ;
2011-02-21 02:02:29 +02:00
}
} else {
2012-11-28 10:41:07 +03:00
ff_draw_rectangle ( frame - > data , frame - > linesize ,
2011-02-21 02:02:29 +02:00
line , pixel_step , hsub , vsub ,
x , y , width , height ) ;
}
}
2013-03-18 22:44:36 +03:00
static int draw_glyphs ( DrawTextContext * s , AVFrame * frame ,
2015-04-11 19:04:25 +02:00
int width , int height ,
const uint8_t rgbcolor [ 4 ] , const uint8_t yuvcolor [ 4 ] ,
int x , int y )
2011-02-22 02:11:35 +02:00
{
2015-07-31 13:49:03 +02:00
char * text = s - > expanded_text ;
2011-02-22 02:11:35 +02:00
uint32_t code = 0 ;
int i ;
uint8_t * p ;
Glyph * glyph = NULL ;
for ( i = 0 , p = text ; * p ; i + + ) {
Glyph dummy = { 0 } ;
GET_UTF8 ( code , * p + + , continue ; ) ;
/* skip new line chars, just go to new line */
if ( code = = ' \n ' | | code = = ' \r ' | | code = = ' \t ' )
continue ;
dummy . code = code ;
2013-03-18 22:44:36 +03:00
glyph = av_tree_find ( s - > glyphs , & dummy , ( void * ) glyph_cmp , NULL ) ;
2011-02-22 02:11:35 +02:00
if ( glyph - > bitmap . pixel_mode ! = FT_PIXEL_MODE_MONO & &
glyph - > bitmap . pixel_mode ! = FT_PIXEL_MODE_GRAY )
return AVERROR ( EINVAL ) ;
2013-03-18 22:44:36 +03:00
if ( s - > is_packed_rgb ) {
2012-11-28 10:41:07 +03:00
draw_glyph_rgb ( frame , & glyph - > bitmap ,
2013-03-18 22:44:36 +03:00
s - > positions [ i ] . x + x , s - > positions [ i ] . y + y , width , height ,
2015-04-11 19:04:25 +02:00
s - > pixel_step [ 0 ] , rgbcolor , s - > rgba_map , s - > alpha ) ;
2011-02-22 02:11:35 +02:00
} else {
2012-11-28 10:41:07 +03:00
draw_glyph_yuv ( frame , & glyph - > bitmap ,
2013-03-18 22:44:36 +03:00
s - > positions [ i ] . x + x , s - > positions [ i ] . y + y , width , height ,
2015-04-11 19:04:25 +02:00
yuvcolor , s - > hsub , s - > vsub , s - > alpha ) ;
2011-02-22 02:11:35 +02:00
}
}
return 0 ;
}
2012-11-28 10:41:07 +03:00
static int draw_text ( AVFilterContext * ctx , AVFrame * frame ,
2011-12-01 13:14:54 +03:00
int width , int height )
{
2013-03-18 22:44:36 +03:00
DrawTextContext * s = ctx - > priv ;
2011-12-01 13:14:54 +03:00
int ret ;
2011-02-21 02:02:29 +02:00
/* draw box */
2013-03-18 22:44:36 +03:00
if ( s - > draw_box )
drawbox ( frame , s - > x , s - > y , s - > w , s - > h ,
s - > box_line , s - > pixel_step , s - > boxcolor ,
s - > hsub , s - > vsub , s - > is_packed_rgb ,
2015-04-11 19:04:25 +02:00
s - > rgba_map , s - > alpha ) ;
2013-03-18 22:44:36 +03:00
if ( s - > shadowx | | s - > shadowy ) {
if ( ( ret = draw_glyphs ( s , frame , width , height ,
s - > shadowcolor_rgba ,
s - > shadowcolor ,
s - > x + s - > shadowx ,
s - > y + s - > shadowy ) ) < 0 )
2011-02-22 02:41:52 +02:00
return ret ;
}
2013-03-18 22:44:36 +03:00
if ( ( ret = draw_glyphs ( s , frame , width , height ,
s - > fontcolor_rgba ,
s - > fontcolor ,
s - > x ,
s - > y ) ) < 0 )
2011-02-22 02:11:35 +02:00
return ret ;
2011-02-21 02:02:29 +02:00
return 0 ;
}
2011-12-01 13:43:11 +03:00
static inline int normalize_double ( int * n , double d )
{
int ret = 0 ;
if ( isnan ( d ) ) {
ret = AVERROR ( EINVAL ) ;
} else if ( d > INT_MAX | | d < INT_MIN ) {
* n = d > INT_MAX ? INT_MAX : INT_MIN ;
ret = AVERROR ( EINVAL ) ;
} else
* n = round ( d ) ;
return ret ;
}
2015-04-11 19:04:25 +02:00
static void update_alpha ( DrawTextContext * s )
{
double alpha = av_expr_eval ( s - > a_pexpr , s - > var_values , & s - > prng ) ;
if ( isnan ( alpha ) )
return ;
if ( alpha > = 1.0 )
s - > alpha = 255 ;
else if ( alpha < = 0 )
s - > alpha = 0 ;
else
s - > alpha = 256 * alpha ;
}
2012-11-28 10:41:07 +03:00
static int filter_frame ( AVFilterLink * inlink , AVFrame * frame )
2011-12-01 13:43:11 +03:00
{
AVFilterContext * ctx = inlink - > dst ;
2013-03-18 22:44:36 +03:00
DrawTextContext * s = ctx - > priv ;
2012-07-08 18:29:42 +03:00
int ret = 0 ;
2011-12-01 13:43:11 +03:00
2012-07-08 18:29:42 +03:00
if ( ( ret = dtext_prepare_text ( ctx ) ) < 0 ) {
2011-12-01 13:43:11 +03:00
av_log ( ctx , AV_LOG_ERROR , " Can't draw text \n " ) ;
2012-11-28 10:41:07 +03:00
av_frame_free ( & frame ) ;
2012-07-08 18:29:42 +03:00
return ret ;
2011-12-01 13:43:11 +03:00
}
2013-03-18 22:44:36 +03:00
s - > var_values [ VAR_T ] = frame - > pts = = AV_NOPTS_VALUE ?
2012-11-27 09:49:45 +03:00
NAN : frame - > pts * av_q2d ( inlink - > time_base ) ;
2013-03-18 22:44:36 +03:00
s - > var_values [ VAR_X ] =
av_expr_eval ( s - > x_pexpr , s - > var_values , & s - > prng ) ;
s - > var_values [ VAR_Y ] =
av_expr_eval ( s - > y_pexpr , s - > var_values , & s - > prng ) ;
s - > var_values [ VAR_X ] =
av_expr_eval ( s - > x_pexpr , s - > var_values , & s - > prng ) ;
s - > draw = av_expr_eval ( s - > d_pexpr , s - > var_values , & s - > prng ) ;
2015-04-11 19:04:25 +02:00
update_alpha ( s ) ;
2013-03-18 22:44:36 +03:00
normalize_double ( & s - > x , s - > var_values [ VAR_X ] ) ;
normalize_double ( & s - > y , s - > var_values [ VAR_Y ] ) ;
if ( s - > fix_bounds ) {
if ( s - > x < 0 ) s - > x = 0 ;
if ( s - > y < 0 ) s - > y = 0 ;
if ( ( unsigned ) s - > x + ( unsigned ) s - > w > inlink - > w )
s - > x = inlink - > w - s - > w ;
if ( ( unsigned ) s - > y + ( unsigned ) s - > h > inlink - > h )
s - > y = inlink - > h - s - > h ;
2012-02-05 15:41:01 +03:00
}
2011-12-01 13:43:11 +03:00
2013-03-18 22:44:36 +03:00
s - > x & = ~ ( ( 1 < < s - > hsub ) - 1 ) ;
s - > y & = ~ ( ( 1 < < s - > vsub ) - 1 ) ;
2011-12-01 13:43:11 +03:00
2015-03-16 10:57:35 +02:00
av_log ( ctx , AV_LOG_TRACE , " n:%d t:%f x:%d y:%d x+w:%d y+h:%d \n " ,
2013-03-18 22:44:36 +03:00
( int ) s - > var_values [ VAR_N ] , s - > var_values [ VAR_T ] ,
s - > x , s - > y , s - > x + s - > w , s - > y + s - > h ) ;
2011-12-01 13:43:11 +03:00
2013-03-18 22:44:36 +03:00
if ( s - > draw )
2012-11-28 10:41:07 +03:00
draw_text ( inlink - > dst , frame , frame - > width , frame - > height ) ;
2011-02-21 02:02:29 +02:00
2013-03-18 22:44:36 +03:00
s - > var_values [ VAR_N ] + = 1.0 ;
2011-12-01 13:43:11 +03:00
2012-11-27 09:49:45 +03:00
return ff_filter_frame ( inlink - > dst - > outputs [ 0 ] , frame ) ;
2011-02-21 02:02:29 +02:00
}
2012-07-24 16:14:01 +03:00
static const AVFilterPad avfilter_vf_drawtext_inputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
. get_video_buffer = ff_null_get_video_buffer ,
2012-11-27 09:49:45 +03:00
. filter_frame = filter_frame ,
2012-07-24 16:14:01 +03:00
. config_props = config_input ,
2012-11-28 10:41:07 +03:00
. needs_writable = 1 ,
2012-07-24 16:14:01 +03:00
} ,
{ NULL }
} ;
static const AVFilterPad avfilter_vf_drawtext_outputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
} ,
{ NULL }
} ;
2013-10-28 09:44:24 +03:00
AVFilter ff_vf_drawtext = {
2011-02-21 02:02:29 +02:00
. name = " drawtext " ,
. description = NULL_IF_CONFIG_SMALL ( " Draw text on top of video frames using libfreetype library. " ) ,
. priv_size = sizeof ( DrawTextContext ) ,
2013-02-25 23:21:29 +03:00
. priv_class = & drawtext_class ,
2011-02-21 02:02:29 +02:00
. init = init ,
. uninit = uninit ,
. query_formats = query_formats ,
2012-07-24 16:14:01 +03:00
. inputs = avfilter_vf_drawtext_inputs ,
. outputs = avfilter_vf_drawtext_outputs ,
2011-02-21 02:02:29 +02:00
} ;