[FFmpeg-devel] [lavfi][aalib][PATCH] lavfi: Proposed changes to vf_drawtext filter to use new fontconfig utils.
Stefano Sabatini
stefasab at gmail.com
Wed Jul 2 01:32:27 CEST 2014
On date Monday 2014-06-30 12:52:45 +0400, iamtakingiteasy at eientei.org encoded:
[...]
> From 61eb8f5813d5d1f1cd4b18b562830f16a24bfb54 Mon Sep 17 00:00:00 2001
> From: Alexander Tumin <iamtakingiteasy at eientei.org>
> Date: Sun, 29 Jun 2014 16:41:45 +0400
> Subject: [PATCH] lavfi: Generalization of freetype/fontconfig font loading.
>
> Introduced new freetypeutils.h/c helper for font loading and discovery
> for video filters.
nit++: lavfi: generalize freetype/fontconfig font loading
Introduce new freetypeutils.h/c helper for font loading and discovery
for video filters.
>
>
> lavfi: Proposed changes to vf_drawtext filter to use new fontconfig utils.
> ---
> Changelog | 1 +
> MAINTAINERS | 1 +
> libavfilter/Makefile | 1 +
> libavfilter/freetypeutils.c | 175 ++++++++++++++++++++++++++++++++++++++++++++
> libavfilter/freetypeutils.h | 109 +++++++++++++++++++++++++++
> libavfilter/version.h | 2 +-
> libavfilter/vf_drawtext.c | 147 ++++---------------------------------
> 7 files changed, 301 insertions(+), 135 deletions(-)
> create mode 100644 libavfilter/freetypeutils.c
> create mode 100644 libavfilter/freetypeutils.h
>
> diff --git a/Changelog b/Changelog
> index 0346877..e51a93a 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -30,6 +30,7 @@ version <next>:
> - zoompan filter
> - signalstats filter
> - hqx filter (hq2x, hq3x, hq4x)
> +- Freetypeutils helper for filters
This really belongs to the internal API, so this shouldn't be
advertised in the changelog.
>
>
> version 2.2:
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d06030b..5f09c9b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -325,6 +325,7 @@ libavfilter
> ===========
>
> Generic parts:
> + freetypeutils.c Alexander Tumin
> graphdump.c Nicolas George
>
> Filters:
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 08817ee..a544ca6 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -122,6 +122,7 @@ OBJS-$(CONFIG_FORMAT_FILTER) += vf_format.o
> OBJS-$(CONFIG_FRAMESTEP_FILTER) += vf_framestep.o
> OBJS-$(CONFIG_FPS_FILTER) += vf_fps.o
> OBJS-$(CONFIG_FRAMEPACK_FILTER) += vf_framepack.o
> +OBJS-$(CONFIG_LIBFREETYPE) += freetypeutils.o
> OBJS-$(CONFIG_FREI0R_FILTER) += vf_frei0r.o
> OBJS-$(CONFIG_GEQ_FILTER) += vf_geq.o
> OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o
> diff --git a/libavfilter/freetypeutils.c b/libavfilter/freetypeutils.c
> new file mode 100644
> index 0000000..c850b18
> --- /dev/null
> +++ b/libavfilter/freetypeutils.c
> @@ -0,0 +1,175 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * 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.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * 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
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "freetypeutils.h"
> +
> +int ff_freetype_init(FFFreetypeContext *ctx, void *avcl)
> +{
> + int err;
> +
> + if (!ctx || !avcl) {
> + return AVERROR(EINVAL);
> + }
> +
> + ctx->avcl = avcl;
> +
> + if ((err = FT_Init_FreeType(&ctx->ft_library))) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not initialize FreeType2: %s\n", FT_ERRMSG(err));
> + return AVERROR(ENOMEM);
> + }
> +
> +
> +#if CONFIG_LIBFONTCONFIG
> + ctx->fc_library = FcInitLoadConfigAndFonts();
> + if (!ctx->fc_library) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not initialize fontconfig\n");
> + return AVERROR(ENOMEM);
> + }
> +#else
> + ctx->fc_library = NULL;
> +#endif
> + return 0;
> +}
> +
> +static int ff_freetype_load_file(FFFreetypeContext *ctx, FT_Face *face, uint8_t *filepath, int index, int logfail)
> +{
> + int err;
> + if ((err = FT_New_Face(ctx->ft_library, filepath, index, face))) {
> + if (logfail) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not load font \"%s\": %s\n", filepath, FT_ERRMSG(err));
> + }
> + return AVERROR(EINVAL);
> + }
> +
> + av_log(ctx->avcl, AV_LOG_INFO, "Loaded font %s\n", filepath);
> + return 0;
> +}
> +
> +#if CONFIG_LIBFONTCONFIG
> +static int ff_freetype_load_fontconfig(FFFreetypeContext *ctx, FT_Face *face, uint8_t *selector, int index)
> +{
> + FcPattern *pattern = NULL, *bestmatch = NULL;
> + FcResult res = FcResultMatch;
> + unsigned char *filepath = 0;
> + int err;
> +
> + pattern = FcNameParse(selector);
> +
> + if (!pattern) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not parse fontconfig pattern\n");
> + return AVERROR(EINVAL);
> + }
> +
> + FcDefaultSubstitute(pattern);
> +
> + if (!FcConfigSubstitute(ctx->fc_library, pattern, FcMatchPattern)) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not perform config substitution for pattern\n");
> + err = AVERROR(ENOMEM);
> + goto fontconfig_cleanup;
> + }
> +
> + bestmatch = FcFontMatch(ctx->fc_library, pattern, &res);
> +
> + if (!bestmatch || res != FcResultMatch) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not find any matches for given pattern.\n");
> + err = AVERROR(ENOENT);
> + goto fontconfig_cleanup;
> + }
> +
> + if (index < 0) {
> + if (FcPatternGetInteger(bestmatch, FC_INDEX, 0, &index) != FcResultMatch) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not find font matching given pattern\n");
> + err = AVERROR(EINVAL);
> + goto fontconfig_cleanup;
> + }
> + }
> + if (FcPatternGetString(bestmatch, FC_FILE, 0, &filepath) != FcResultMatch) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not find font matching given pattern\n");
> + err =AVERROR(EINVAL);
> + goto fontconfig_cleanup;
> + }
> +
> + err = ff_freetype_load_file(ctx, face, filepath, index, 1);
> +
> +fontconfig_cleanup:
> + if (pattern) {
> + FcPatternDestroy(pattern);
> + }
> +
> + if (bestmatch) {
> + FcPatternDestroy(bestmatch);
> + }
> +
> + return err;
> +}
> +#endif
> +
> +int ff_freetype_load(FFFreetypeContext *ctx, FT_Face *face, uint8_t *selector, int index)
> +{
> + int err;
> +
> + if (!ctx || !face || !selector) {
> + return AVERROR(EINVAL);
> + }
> +
> + err = ff_freetype_load_file(ctx, face, selector, (index < 0) ? 0 : index, !CONFIG_LIBFONTCONFIG);
> +
> + if (!err) {
> + return 0;
> + }
> +
> +#if CONFIG_LIBFONTCONFIG
> + err = ff_freetype_load_fontconfig(ctx, face, selector, index);
> +#endif
> +
> + return err;
> +}
> +
> +int ff_freetype_stroker_new(FFFreetypeContext *ctx, FT_Stroker *stroker)
> +{
> + int err;
> +
> + if (!ctx || !stroker) {
> + return AVERROR(EINVAL);
> + }
> +
> + if ((err = FT_Stroker_New(ctx->ft_library, stroker))) {
> + av_log(ctx->avcl, AV_LOG_ERROR, "Could not create new stroker: %s\n", FT_ERRMSG(err));
> + return AVERROR_EXTERNAL;
> + }
> +
> + return 0;
> +}
> +
> +void ff_freetype_uninit(FFFreetypeContext *ctx)
> +{
> + if (!ctx) {
> + return;
> + }
> +
> + ctx->avcl = NULL;
> +
> + FT_Done_FreeType(ctx->ft_library);
> +
> +#if CONFIG_LIBFONTCONFIG
> + if (ctx->fc_library) {
> + FcConfigDestroy(ctx->fc_library);
> + }
> + ctx->fc_library = NULL;
> +#endif
> +}
> diff --git a/libavfilter/freetypeutils.h b/libavfilter/freetypeutils.h
> new file mode 100644
> index 0000000..00dac0c
> --- /dev/null
> +++ b/libavfilter/freetypeutils.h
> @@ -0,0 +1,109 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * 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.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * 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
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVFILTER_FREETYPEUTILS_H
> +#define AVFILTER_FREETYPEUTILS_H
> +
> +/**
> + * @file
> + * FreeType2/Fontconfig font loading utilities
> + */
> +
> +#include "config.h"
> +
> +#include "internal.h"
> +
> +#include <ft2build.h>
> +#include FT_FREETYPE_H
> +#include FT_GLYPH_H
> +#include FT_STROKER_H
> +
> +#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
> +
> +#if CONFIG_LIBFONTCONFIG
> +#include <fontconfig/fontconfig.h>
> +#define FCTYPE FcConfig
> +#else
> +#define FCTYPE void
> +#endif
> +
> +typedef struct FFFreetypeContext {
> + void *avcl; ///< A pointer to an arbitrary struct of which the first field is a pointer to an AVClass struct.
> + FT_Library ft_library; ///< FreeType library handle
> + FCTYPE *fc_library; ///< Fontconfig library handle
> +} FFFreetypeContext;
> +
> +/**
> + * Init a freetype context
Nit: missing ending point.
> + *
> + * If fontconfig selected at build system configuration time, additional fontconfig
> + * matcher is enabled.
> + *
> + * @param ctx freetype context
> + * @param avcl logging context, a pointer to an arbitrary struct of which the first field is a pointer to an AVClass struct.
> + *
> + * @return 0 on success or < 0 on failure.
here you can remove the ending points
> + */
> +int ff_freetype_init(FFFreetypeContext *ctx, void *avcl);
> +
> +/**
> + * Load a font for given sellector, which can be either:
sellector typo
> + * - a filepath, or
> + * - a fontconfig pattern in FcNameUnparse format.
> + * Note that FcNameUnparse function returns value, which suposed to be
> + * free'd by you.
freed?
> + *
> + * @param ctx font context
> + * @param face font face to populate on successful font match.
> + * You will have to free it with FT_Done_Face afterwards.
> + * @param selector either filepath or a fontconfig pattern (if available)
> + * @param index font index, use -1 to get default font
> + *
> + * @return 0 on success or < 0 on failure.
you can drop final dot
> + *
> + */
> +int ff_freetype_load(FFFreetypeContext *ctx, FT_Face *face, uint8_t *selector, int index);
> +
> +/**
> + * Allocate a new FT_Stroker using wrapped FT_Library.
> + *
> + * @param ctx font context
> + * @param stroker stroker to allocate.
> + * Yoy will have to free it with FT_Stroker_Done afterwards.
yoy
> + *
> + * @return 0 on success or < 0 on failure.
nit: ditto
> + */
> +int ff_freetype_stroker_new(FFFreetypeContext *ctx, FT_Stroker *stroker);
> +
> +/**
> + * Uninit a freetype context
missing final point, or you can skip the doxy altogether in this case.
> + */
> +void ff_freetype_uninit(FFFreetypeContext *ctx);
> +
> +#endif /* AVFILTER_FREETYPEUTILS_H */
> diff --git a/libavfilter/version.h b/libavfilter/version.h
> index f125032..bf9191e 100644
> --- a/libavfilter/version.h
> +++ b/libavfilter/version.h
> @@ -30,7 +30,7 @@
> #include "libavutil/version.h"
>
> #define LIBAVFILTER_VERSION_MAJOR 4
> -#define LIBAVFILTER_VERSION_MINOR 9
> +#define LIBAVFILTER_VERSION_MINOR 10
> #define LIBAVFILTER_VERSION_MICRO 100
>
> #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
> diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
> index adc587e..a973b19 100644
> --- a/libavfilter/vf_drawtext.c
> +++ b/libavfilter/vf_drawtext.c
> @@ -36,10 +36,6 @@
> #include <time.h>
> #include <unistd.h>
>
> -#if CONFIG_LIBFONTCONFIG
> -#include <fontconfig/fontconfig.h>
> -#endif
> -
> #include "libavutil/avstring.h"
> #include "libavutil/bprint.h"
> #include "libavutil/common.h"
> @@ -54,14 +50,10 @@
> #include "avfilter.h"
> #include "drawutils.h"
> #include "formats.h"
> +#include "freetypeutils.h"
> #include "internal.h"
> #include "video.h"
>
> -#include <ft2build.h>
> -#include FT_FREETYPE_H
> -#include FT_GLYPH_H
> -#include FT_STROKER_H
> -
> static const char *const var_names[] = {
> "dar",
> "hsub", "vsub",
> @@ -131,7 +123,7 @@ typedef struct DrawTextContext {
> enum expansion_mode exp_mode; ///< expansion mode to use for the text
> int reinit; ///< tells if the filter is being reinited
> #if CONFIG_LIBFONTCONFIG
> - uint8_t *font; ///< font to be used
> + uint8_t *font; ///< font to be used
> #endif
> uint8_t *fontfile; ///< font to be used
> uint8_t *text; ///< text to be drawn
> @@ -159,7 +151,7 @@ typedef struct DrawTextContext {
> FFDrawColor bordercolor; ///< border color
> FFDrawColor boxcolor; ///< background color
>
> - FT_Library library; ///< freetype font library handle
> + FFFreetypeContext font_ctx; ///< freetype font library handle
freetype_ctx or ft_ctx looks more descriptive
> FT_Face face; ///< freetype font face handle
> FT_Stroker stroker; ///< freetype stroker handle
> struct AVTreeNode *glyphs; ///< rendered glyphs, stored using the UTF-32 char code
> @@ -246,20 +238,6 @@ static const AVOption drawtext_options[]= {
>
> AVFILTER_DEFINE_CLASS(drawtext);
>
> -#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
> -
> typedef struct Glyph {
> FT_Glyph *glyph;
> uint32_t code;
> @@ -348,110 +326,6 @@ error:
> return ret;
> }
>
> -static int load_font_file(AVFilterContext *ctx, const char *path, int index)
> -{
> - DrawTextContext *s = ctx->priv;
> - int err;
> -
> - err = FT_New_Face(s->library, path, index, &s->face);
> - if (err) {
> - av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
> - s->fontfile, FT_ERRMSG(err));
> - return AVERROR(EINVAL);
> - }
> - return 0;
> -}
> -
> -#if CONFIG_LIBFONTCONFIG
> -static int load_font_fontconfig(AVFilterContext *ctx)
> -{
> - DrawTextContext *s = ctx->priv;
> - FcConfig *fontconfig;
> - FcPattern *pat, *best;
> - FcResult result = FcResultMatch;
> - FcChar8 *filename;
> - int index;
> - double size;
> - int err = AVERROR(ENOENT);
> -
> - fontconfig = FcInitLoadConfigAndFonts();
> - if (!fontconfig) {
> - av_log(ctx, AV_LOG_ERROR, "impossible to init fontconfig\n");
> - return AVERROR_UNKNOWN;
> - }
> - pat = FcNameParse(s->fontfile ? s->fontfile :
> - (uint8_t *)(intptr_t)"default");
> - if (!pat) {
> - av_log(ctx, AV_LOG_ERROR, "could not parse fontconfig pat");
> - return AVERROR(EINVAL);
> - }
> -
> - FcPatternAddString(pat, FC_FAMILY, s->font);
> - if (s->fontsize)
> - FcPatternAddDouble(pat, FC_SIZE, (double)s->fontsize);
> -
> - FcDefaultSubstitute(pat);
> -
> - if (!FcConfigSubstitute(fontconfig, pat, FcMatchPattern)) {
> - av_log(ctx, AV_LOG_ERROR, "could not substitue fontconfig options"); /* very unlikely */
> - FcPatternDestroy(pat);
> - return AVERROR(ENOMEM);
> - }
> -
> - best = FcFontMatch(fontconfig, pat, &result);
> - FcPatternDestroy(pat);
> -
> - if (!best || result != FcResultMatch) {
> - av_log(ctx, AV_LOG_ERROR,
> - "Cannot find a valid font for the family %s\n",
> - s->font);
> - goto fail;
> - }
> -
> - if (
> - FcPatternGetInteger(best, FC_INDEX, 0, &index ) != FcResultMatch ||
> - FcPatternGetDouble (best, FC_SIZE, 0, &size ) != FcResultMatch) {
> - av_log(ctx, AV_LOG_ERROR, "impossible to find font information");
> - return AVERROR(EINVAL);
> - }
> -
> - if (FcPatternGetString(best, FC_FILE, 0, &filename) != FcResultMatch) {
> - av_log(ctx, AV_LOG_ERROR, "No file path for %s\n",
> - s->font);
> - goto fail;
> - }
> -
> - av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
> - if (!s->fontsize)
> - s->fontsize = size + 0.5;
> -
> - err = load_font_file(ctx, filename, index);
> - if (err)
> - return err;
> - FcConfigDestroy(fontconfig);
> -fail:
> - FcPatternDestroy(best);
> - return err;
> -}
> -#endif
> -
> -static int load_font(AVFilterContext *ctx)
> -{
> - DrawTextContext *s = ctx->priv;
> - int err;
> -
> - /* load the face, and set up the encoding, which is by default UTF-8 */
> - err = load_font_file(ctx, s->fontfile, 0);
> - if (!err)
> - return 0;
> -#if CONFIG_LIBFONTCONFIG
> - err = load_font_fontconfig(ctx);
> - if (!err)
> - return 0;
> -#endif
> - return err;
> -}
> -
> static int load_textfile(AVFilterContext *ctx)
> {
> DrawTextContext *s = ctx->priv;
> @@ -526,13 +400,18 @@ static av_cold int init(AVFilterContext *ctx)
> return AVERROR(EINVAL);
> }
>
> - if ((err = FT_Init_FreeType(&(s->library)))) {
> + if (ff_freetype_init(&s->font_ctx, ctx)) {
> av_log(ctx, AV_LOG_ERROR,
> - "Could not load FreeType: %s\n", FT_ERRMSG(err));
> + "Could not load FreeType\n");
> return AVERROR(EINVAL);
if ((ret = ff_freetype_init(&s->font_ctx, ctx)) < 0)
return ret;
???
Assuming the log is redundant in this case.
>
> - err = load_font(ctx);
> +#if CONFIG_LIBFONTCONFIG
> + err = ff_freetype_load(&s->font_ctx, &s->face, (s->fontfile == 0) ? s->font : s->fontfile, -1);
> +#else
> + err = ff_freetype_load(&s->font_ctx, &s->face, s->fontfile, -1);
> +#endif
> +
> if (err)
> return err;
> if (!s->fontsize)
> @@ -544,7 +423,7 @@ static av_cold int init(AVFilterContext *ctx)
> }
>
> if (s->borderw) {
> - if (FT_Stroker_New(s->library, &s->stroker)) {
> + if (ff_freetype_stroker_new(&s->font_ctx, &s->stroker)) {
> av_log(ctx, AV_LOG_ERROR, "Coult not init FT stroker\n");
> return AVERROR_EXTERNAL;
If you're using an internal function you should return its error code.
Also you could probably create the stroker when initing the context.
[...]
--
FFmpeg = Faithless and Funny Majestic Proud Extended Gigant
More information about the ffmpeg-devel
mailing list