[FFmpeg-devel] [PATCH] Add drawtext filter from the libavfilter soc repo.
Stefano Sabatini
stefano.sabatini-lala
Sun Feb 20 00:10:17 CET 2011
On date Saturday 2011-02-19 15:21:13 +0000, M?ns Rullg?rd encoded:
> Stefano Sabatini <stefano.sabatini-lala at poste.it> writes:
[...]
> > + /* load ASCII chars */
> > + for (code = 0; code < 256; code++)
> > + load_glyph(ctx, code, &y_min, &y_max);
>
> ASCII ends at 127. The remainder up to 255 is latin1 aka 8859-1.
>
> > + /* load all the other non-ASCII chars presented in the UTF-8 sequence */
> > + for (p = dtext->text; *p; ) {
> > + GET_UTF8(code, *p++, continue;);
> > + load_glyph(ctx, code, &y_min, &y_max);
> > + }
>
> Why do you preload the entire latin1 character set? I think it would be
> better to load glyphs on demand as they are encountered while
> rendering. That would save memory and speed up lookup (since the tree
> will be shallower). Your current version will also fail if the strftime
> expansion introduces non-latin1 characters not used elsewhere in the
> format string. On-demand loading would fix that as well.
Yes I was aware of the problem, now it should be fixed, all the glyphs
are loaded and cached lazily when found for the first time in the
expanded string.
> > + dtext->text_height = y_max - y_min;
> > + dtext->baseline = y_max;
> > +
> > +#if !HAVE_LOCALTIME_R
> > + av_log(ctx, AV_LOG_WARNING, "strftime() expansion unavailable!\n");
> > +#endif
> > +
> > + return 0;
> > +}
> > +
> > +static int query_formats(AVFilterContext *ctx)
> > +{
> > + /* FIXME: Add support for other formats */
> > + enum PixelFormat pix_fmts[] = {
>
> static const
Fixed.
> > + PIX_FMT_YUV420P, PIX_FMT_YUV444P, PIX_FMT_YUV422P,
> > + PIX_FMT_YUV411P, PIX_FMT_YUV410P,
> > + PIX_FMT_YUV440P, PIX_FMT_NONE
> > + };
> > +
> > + avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
> > + return 0;
> > +}
> > +
> > +static int glyph_enu_free(void *opaque, void *elem)
> > +{
> > + av_free(elem);
> > + return 0;
> > +}
> > +
> > +static av_cold void uninit(AVFilterContext *ctx)
> > +{
> > + DrawTextContext *dtext = ctx->priv;
> > + av_freep(&dtext->fontfile);
> > + av_freep(&dtext->text);
> > + av_freep(&dtext->fgcolor_string);
> > + av_freep(&dtext->bgcolor_string);
> > + av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
> > + av_tree_destroy(dtext->glyphs);
> > + dtext->glyphs = 0;
> > + FT_Done_Face(dtext->face);
> > + FT_Done_FreeType(dtext->library);
> > +}
>
> Do those FT functions handle null pointers?
Yes, checked, they do at least with my version of libfreetype (but not
documented in the FT API).
> [...]
>
> > +static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
> > + int width, int height)
> > +{
> > + DrawTextContext *dtext = ctx->priv;
> > + FT_Face face = dtext->face;
> > + char *text = dtext->text;
> > + uint32_t code = 0;
> > + int x = 0, y = 0, i = 0;
> > + uint8_t *p;
> > + int str_w, str_w_max;
> > + FT_Vector delta;
> > + Glyph *glyph = NULL, *prev_glyph = NULL;
> > + Glyph dummy = { 0 };
> > + Glyph *glyph0 = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
> > +
> > +#if HAVE_LOCALTIME_R
> > + time_t now = time(0);
> > + struct tm ltime;
> > + size_t expanded_text_len;
> > +
> > + dtext->expanded_text[0] = '\1';
> > + expanded_text_len = strftime(dtext->expanded_text, MAX_EXPANDED_TEXT_SIZE,
> > + text, localtime_r(&now, <ime));
> > +
> > + if (expanded_text_len == 0 && dtext->expanded_text[0] != '\0') {
>
> This isn't bullet-proof. The buffer contents are unspecified if the
> return value is zero. It is safe insofar an unterminated string will
> never escape, but it doesn't necessarily catch all errors. Consider
> this remark merely as such.
Uhm OK, blame the fools who designed the strftime interface...
> > + av_log(ctx, AV_LOG_WARNING,
> > + "String expanded by strftime() is too big");
> > + return AVERROR(EINVAL);
> > + }
> > + text = dtext->expanded_text;
> > +#endif
> > +
> > + /* measure text size and save glyphs position */
> > + str_w = str_w_max = 0;
> > + x = dtext->x;
> > + y = dtext->y;
> > +
> > + for (i = 0, p = text; *p; i++) {
> > + GET_UTF8(code, *p++, continue;);
> > +
> > + /* get glyph */
> > + prev_glyph = glyph;
> > + dummy.code = code;
> > + glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
> > + if (!glyph)
> > + glyph = glyph0;
> > +
> > + /* kerning */
> > + if (dtext->use_kerning && prev_glyph && glyph->code) {
> > + FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
> > + ft_kerning_default, &delta);
> > + x += delta.x >> 6;
> > + }
> > +
> > + if (x + glyph->advance >= width || code == '\n' || code == '\r') {
> > + if (code != '\n' || code != '\r')
> > + str_w_max = width - dtext->x - 1;
> > + y += dtext->text_height;
> > + x = dtext->x;
> > + }
>
> This will double-space windows-style lines. You could probably get away
> with ignoring \r and advancing the line on \n only. A strict solution
> that also works with old-style Mac (\r only) lines is more complex, but
> we don't run on such systems anyway.
Should be fixed, I tested with \n and \r\n, should also work with \r.
[...]
> > + /* draw glyphs */
> > + for (i = 0, p = text; *p; i++) {
> > + Glyph dummy = { 0 };
> > + GET_UTF8(code, *p++, continue;);
> > +
> > + /* skip new line char, just go to new line */
> > + if (code == '\n' || code == '\r')
> > + continue;
> > +
> > + dummy.code = code;
> > + glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
> > + if (!glyph)
> > + glyph = glyph0;
> > +
> > + draw_glyph(picref, &glyph->bitmap,
> > + dtext->positions[i].x, dtext->positions[i].y, width, height,
> > + dtext->fgcolor, dtext->bgcolor, dtext->outline,
> > + dtext->hsub, dtext->vsub);
> > +
> > + /* increment pen position */
> > + x += face->glyph->advance.x >> 6;
> > + }
>
> You might want to do something clever with the tab character. I don't
> really have a good suggestion though.
Skipped for now, we could add an option for setting the tab size.
Other changes: I *removed* the code for rendering the font outline,
the result was honestly rather poor, this also allowed to make more
clear the interface (fgcolor -> fontcolor, bgcolor -> boxcolor).
Again on the interface side, I wonder if it is a good idea to keep the
ftload flags, the only useful flag which I can discern amongst the
many flags is +monochrome, but it may be enabled with monochrome=1 in
the options string.
Comments?
Still missing: docs update and RGB support, it should not be too
complicate to add it (but I can eventually do it as a separate patch).
--
FFmpeg = Faithful Free Multipurpose Picky Ermetic Glue
More information about the ffmpeg-devel
mailing list