summaryrefslogtreecommitdiff
path: root/libavfilter
diff options
context:
space:
mode:
authorMichael Niedermayer <michaelni@gmx.at>2011-05-08 03:29:51 +0200
committerMichael Niedermayer <michaelni@gmx.at>2011-05-08 03:29:51 +0200
commit6d6b0c96fdc576f4643e3d4066e5ebebdb693ade (patch)
tree3689102a30dc0676ec66f3363afa70aa2824d12e /libavfilter
parent732ff29bff1b42c9471d7aa699efae6ffd1b5d61 (diff)
parent994de197c0d5dcf38d5ab68f8f03f37a890a4458 (diff)
Merge remote branch 'qatar/master'
* qatar/master: drawtext: add documentation for the shadow parameters drawtext: add shadow support. drawtext: factor draw_glyphs. drawtext: fix and optimize yuv blend. drawtext: get rid of divisions in the inner loop. drawtext: simplify chroma blend and fix chroma alpha. lavfi: Port drawtext filter by Hemanth from the libavfilter soc repo Conflicts: configure libavfilter/avfilter.h libavfilter/vf_drawtext.c Merged-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavfilter')
-rw-r--r--libavfilter/vf_drawtext.c174
1 files changed, 99 insertions, 75 deletions
diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index 99045b7b4b..b26029bb8f 100644
--- a/libavfilter/vf_drawtext.c
+++ b/libavfilter/vf_drawtext.c
@@ -45,17 +45,13 @@
#include FT_FREETYPE_H
#include FT_GLYPH_H
-#define MAX_EXPANDED_TEXT_SIZE 2048
-
typedef struct {
const AVClass *class;
- char *fontfile; ///< font to be used
- char *text; ///< text to be drawn
+ uint8_t *fontfile; ///< font to be used
+ uint8_t *text; ///< text to be drawn
+ uint8_t *text_priv; ///< used to detect whether text changed
int ft_load_flags; ///< flags used for loading fonts, see FT_LOAD_*
- /** buffer containing the text expanded by strftime */
- char expanded_text[MAX_EXPANDED_TEXT_SIZE];
- /** positions for each element in the text */
- FT_Vector positions[MAX_EXPANDED_TEXT_SIZE];
+ FT_Vector *positions; ///< positions for each element in the text
char *textfile; ///< file with text to be drawn
unsigned int x; ///< x position to start drawing text
unsigned int y; ///< y position to start drawing text
@@ -157,9 +153,10 @@ typedef struct {
int bitmap_top;
} Glyph;
-static int glyph_cmp(const Glyph *a, const Glyph *b)
+static int glyph_cmp(void *key, const void *b)
{
- int64_t diff = (int64_t)a->code - (int64_t)b->code;
+ 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;
}
@@ -169,21 +166,26 @@ static int glyph_cmp(const Glyph *a, const Glyph *b)
static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
{
DrawTextContext *dtext = ctx->priv;
- Glyph *glyph = av_mallocz(sizeof(Glyph));
+ Glyph *glyph;
struct AVTreeNode *node = NULL;
int ret;
/* load glyph into dtext->face->glyph */
- ret = FT_Load_Char(dtext->face, code, dtext->ft_load_flags);
- if (ret)
+ if (FT_Load_Char(dtext->face, code, dtext->ft_load_flags))
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;
- glyph->glyph = av_mallocz(sizeof(FT_Glyph));
- ret = FT_Get_Glyph(dtext->face->glyph, glyph->glyph);
- if (ret)
- return AVERROR(EINVAL);
+
+ if (FT_Get_Glyph(dtext->face->glyph, glyph->glyph)) {
+ ret = AVERROR(EINVAL);
+ goto error;
+ }
glyph->bitmap = dtext->face->glyph->bitmap;
glyph->bitmap_left = dtext->face->glyph->bitmap_left;
@@ -194,19 +196,29 @@ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
/* cache the newly created glyph */
- if (!node)
- node = av_mallocz(av_tree_node_size);
- av_tree_insert(&dtext->glyphs, glyph, (void *)glyph_cmp, &node);
+ if (!(node = av_mallocz(av_tree_node_size))) {
+ ret = AVERROR(ENOMEM);
+ goto error;
+ }
+ av_tree_insert(&dtext->glyphs, glyph, glyph_cmp, &node);
if (glyph_ptr)
*glyph_ptr = glyph;
return 0;
+
+error:
+ if (glyph)
+ av_freep(&glyph->glyph);
+ av_freep(&glyph);
+ av_freep(&node);
+ return ret;
}
static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
{
int err;
DrawTextContext *dtext = ctx->priv;
+ Glyph *glyph;
dtext->class = &drawtext_class;
av_opt_set_defaults2(dtext, 0, 0);
@@ -294,14 +306,15 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
/* 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;
+ }
+ dtext->tabsize *= glyph->advance;
+
#if !HAVE_LOCALTIME_R
av_log(ctx, AV_LOG_WARNING, "strftime() expansion unavailable!\n");
-#else
- if (strlen(dtext->text) >= MAX_EXPANDED_TEXT_SIZE) {
- av_log(ctx, AV_LOG_ERROR,
- "Impossible to print text, string is too big\n");
- return AVERROR(EINVAL);
- }
#endif
return 0;
@@ -338,6 +351,7 @@ static av_cold void uninit(AVFilterContext *ctx)
av_freep(&dtext->text);
av_freep(&dtext->fontcolor_string);
av_freep(&dtext->boxcolor_string);
+ av_freep(&dtext->positions);
av_freep(&dtext->shadowcolor_string);
av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
av_tree_destroy(dtext->glyphs);
@@ -393,7 +407,7 @@ static int config_input(AVFilterLink *inlink)
luma_pos = ((x) ) + ((y) ) * picref->linesize[0]; \
alpha = yuva_color[3] * (val) * 129; \
picref->data[0][luma_pos] = (alpha * yuva_color[0] + (255*255*129 - alpha) * picref->data[0][luma_pos] ) >> 23; \
- if(((x) & ((1<<(hsub))-1))==0 && ((y) & ((1<<(vsub))-1))==0){\
+ if (((x) & ((1<<(hsub)) - 1)) == 0 && ((y) & ((1<<(vsub)) - 1)) == 0) {\
chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[1]; \
chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[2]; \
picref->data[1][chroma_pos1] = (alpha * yuva_color[1] + (255*255*129 - alpha) * picref->data[1][chroma_pos1]) >> 23; \
@@ -403,7 +417,7 @@ static int config_input(AVFilterLink *inlink)
static inline int draw_glyph_yuv(AVFilterBufferRef *picref, FT_Bitmap *bitmap, unsigned int x,
unsigned int y, unsigned int width, unsigned int height,
- unsigned char yuva_color[4], int hsub, int vsub)
+ const uint8_t yuva_color[4], int hsub, int vsub)
{
int r, c, alpha;
unsigned int luma_pos, chroma_pos1, chroma_pos2;
@@ -439,7 +453,7 @@ static inline int draw_glyph_yuv(AVFilterBufferRef *picref, FT_Bitmap *bitmap, u
static inline int draw_glyph_rgb(AVFilterBufferRef *picref, FT_Bitmap *bitmap,
unsigned int x, unsigned int y,
unsigned int width, unsigned int height, int pixel_step,
- unsigned char rgba_color[4], uint8_t rgba_map[4])
+ const uint8_t rgba_color[4], const uint8_t rgba_map[4])
{
int r, c, alpha;
uint8_t *p;
@@ -495,10 +509,15 @@ static inline void drawbox(AVFilterBufferRef *picref, unsigned int x, unsigned i
}
}
+static inline int is_newline(uint32_t c)
+{
+ return (c == '\n' || c == '\r' || c == '\f' || c == '\v');
+}
+
static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
int width, int height, const uint8_t rgbcolor[4], const uint8_t yuvcolor[4], int x, int y)
{
- char *text = HAVE_LOCALTIME_R ? dtext->expanded_text : dtext->text;
+ char *text = dtext->text;
uint32_t code = 0;
int i;
uint8_t *p;
@@ -537,44 +556,53 @@ static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
int width, int height)
{
DrawTextContext *dtext = ctx->priv;
- char *text = dtext->text;
uint32_t code = 0, prev_code = 0;
int x = 0, y = 0, i = 0, ret;
int text_height, baseline;
uint8_t *p;
- int str_w, str_w_max;
+ int str_w = 0;
int y_min = 32000, y_max = -32000;
FT_Vector delta;
Glyph *glyph = NULL, *prev_glyph = NULL;
Glyph dummy = { 0 };
+ if (dtext->text != dtext->text_priv) {
#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, &ltime));
- text = dtext->expanded_text;
- if (expanded_text_len == 0 && dtext->expanded_text[0] != '\0') {
- av_log(ctx, AV_LOG_ERROR,
- "Impossible to print text, string is too big\n");
- return AVERROR(EINVAL);
- }
+ time_t now = time(0);
+ struct tm ltime;
+ uint8_t *buf = NULL;
+ int buflen = 2*strlen(dtext->text) + 1, len;
+
+ localtime_r(&now, &ltime);
+
+ while ((buf = av_realloc(buf, buflen))) {
+ *buf = 1;
+ if ((len = strftime(buf, buflen, dtext->text, &ltime)) != 0 || *buf == 0)
+ break;
+ buflen *= 2;
+ }
+ if (!buf)
+ return AVERROR(ENOMEM);
+ av_freep(&dtext->text);
+ dtext->text = dtext->text_priv = buf;
+#else
+ dtext->text_priv = dtext->text;
#endif
+ if (!(dtext->positions = av_realloc(dtext->positions,
+ strlen(dtext->text)*sizeof(*dtext->positions))))
+ return AVERROR(ENOMEM);
+ }
- str_w = str_w_max = 0;
x = dtext->x;
y = dtext->y;
/* load and cache glyphs */
- for (i = 0, p = text; *p; i++) {
+ for (i = 0, p = dtext->text; *p; i++) {
GET_UTF8(code, *p++, continue;);
/* get glyph */
dummy.code = code;
- glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
+ glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
if (!glyph)
load_glyph(ctx, &glyph, code);
@@ -586,17 +614,25 @@ static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
/* compute and save position for each glyph */
glyph = NULL;
- for (i = 0, p = text; *p; i++) {
+ for (i = 0, p = dtext->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)) {
+ str_w = FFMAX(str_w, x - dtext->x);
+ y += text_height;
+ x = dtext->x;
+ continue;
+ }
+
/* get glyph */
prev_glyph = glyph;
dummy.code = code;
- glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
+ glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
/* kerning */
if (dtext->use_kerning && prev_glyph && glyph->code) {
@@ -605,9 +641,8 @@ static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
x += delta.x >> 6;
}
- if (x + glyph->advance >= width || code == '\r' || code == '\n') {
- if (x + glyph->advance >= width)
- str_w_max = width - dtext->x - 1;
+ if (x + glyph->bbox.xMax >= width) {
+ str_w = FFMAX(str_w, x - dtext->x);
y += text_height;
x = dtext->x;
}
@@ -615,38 +650,27 @@ static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
/* save position */
dtext->positions[i].x = x + glyph->bitmap_left;
dtext->positions[i].y = y - glyph->bitmap_top + baseline;
- if (code != '\n' && code != '\r') {
- int advance = glyph->advance;
- if (code == '\t')
- advance *= dtext->tabsize;
- x += advance;
- str_w += advance;
- }
- prev_code = code;
+ if (code == '\t') x = (x / dtext->tabsize + 1)*dtext->tabsize;
+ else x += glyph->advance;
}
- y += text_height;
- if (str_w_max == 0)
- str_w_max = str_w;
+ str_w = FFMIN(width - dtext->x - 1, FFMAX(str_w, x - dtext->x));
+ y = FFMIN(y + text_height, height - 1);
/* draw box */
- if (dtext->draw_box) {
- /* check if it doesn't pass the limits */
- str_w_max = FFMIN(str_w_max, width - dtext->x - 1);
- y = FFMIN(y, height - 1);
-
- /* draw background */
- drawbox(picref, dtext->x, dtext->y, str_w_max, y-dtext->y,
+ if (dtext->draw_box)
+ drawbox(picref, dtext->x, dtext->y, str_w, y-dtext->y,
dtext->box_line, dtext->pixel_step, dtext->boxcolor,
dtext->hsub, dtext->vsub, dtext->is_packed_rgb, dtext->rgba_map);
- }
- if(dtext->shadowx || dtext->shadowy){
- if((ret=draw_glyphs(dtext, picref, width, height, dtext->shadowcolor_rgba, dtext->shadowcolor, dtext->shadowx, dtext->shadowy))<0)
+ if (dtext->shadowx || dtext->shadowy) {
+ if ((ret = draw_glyphs(dtext, picref, width, height, dtext->shadowcolor_rgba,
+ dtext->shadowcolor, dtext->shadowx, dtext->shadowy)) < 0)
return ret;
}
- if((ret=draw_glyphs(dtext, picref, width, height, dtext->fontcolor_rgba, dtext->fontcolor, 0, 0))<0)
+ if ((ret = draw_glyphs(dtext, picref, width, height, dtext->fontcolor_rgba,
+ dtext->fontcolor, 0, 0)) < 0)
return ret;
return 0;