History

avfilter/vf_drawtext: improve glyph shaping and positioning

- text is now shaped using libharfbuz - glyphs position is now accurate to 1/4 pixel in both directions - the default line height is now the one defined in the font Adds libharfbuzz dependency.

yethie committed 3 years ago

Commit 1eeb59a1 parent ba00ed33 files · +592 −280Browse files

Showing the changes to libavfilter (1 of 3 files). Show all

Unified · Split

libavfilter/vf_drawtext.c+583−276
@@ -1,4 +1,5 @@
1/*1/*
2 * Copyright (c) 2023 Francesco Carusi
2 * Copyright (c) 2011 Stefano Sabatini3 * Copyright (c) 2011 Stefano Sabatini
3 * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram4 * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram
4 * Copyright (c) 2003 Gustavo Sverzut Barbieri <gsbarbieri@yahoo.com.br>5 * Copyright (c) 2003 Gustavo Sverzut Barbieri <gsbarbieri@yahoo.com.br>
@@ -72,10 +73,16 @@ · 67 unmodified lines
72#include FT_GLYPH_H73#include FT_GLYPH_H
73#include FT_STROKER_H74#include FT_STROKER_H
74 75
76#include <hb.h>
77#include <hb-ft.h>
78
79// Ceiling operation for positive integers division
80#define POS_CEIL(x, y) ((x)/(y) + ((x)%(y) != 0))
81
75static const char *const var_names[] = {82static const char *const var_names[] = {
76 "dar",83 "dar",
77 "hsub", "vsub",84 "hsub", "vsub",
78 "line_h", "lh", ///< line height, same as max_glyph_h85 "line_h", "lh", ///< line height
79 "main_h", "h", "H", ///< height of the input video86 "main_h", "h", "H", ///< height of the input video
80 "main_w", "w", "W", ///< width of the input video87 "main_w", "w", "W", ///< width of the input video
81 "max_glyph_a", "ascent", ///< max glyph ascender88 "max_glyph_a", "ascent", ///< max glyph ascender
@@ -156,6 +163,72 @@ enum expansion_mode { · 74 unmodified lines
156 EXP_STRFTIME,163 EXP_STRFTIME,
157};164};
158 165
166typedef struct HarfbuzzData {
167 hb_buffer_t* buf;
168 hb_font_t* font;
169 unsigned int glyph_count;
170 hb_glyph_info_t* glyph_info;
171 hb_glyph_position_t* glyph_pos;
172} HarfbuzzData;
173
174/** Information about a single glyph in a text line */
175typedef struct GlyphInfo {
176 uint32_t code; ///< the glyph code point
177 int x; ///< the x position of the glyph
178 int y; ///< the y position of the glyph
179 int shift_x64; ///< the horizontal shift of the glyph in 26.6 units
180 int shift_y64; ///< the vertical shift of the glyph in 26.6 units
181} GlyphInfo;
182
183/** Information about a single line of text */
184typedef struct TextLine {
185 int offset_left64; ///< offset between the origin and
186 /// the leftmost pixel of the first glyph
187 int offset_right64; ///< maximum offset between the origin and
188 /// the rightmost pixel of the last glyph
189 int width64; ///< width of the line
190 HarfbuzzData hb_data; ///< libharfbuzz data of this text line
191 GlyphInfo* glyphs; ///< array of glyphs in this text line
192 int cluster_offset; ///< the offset at which this line begins
193} TextLine;
194
195/** A glyph as loaded and rendered using libfreetype */
196typedef struct Glyph {
197 FT_Glyph glyph;
198 FT_Glyph border_glyph;
199 uint32_t code;
200 unsigned int fontsize;
201 /** Glyph bitmaps with 1/4 pixel precision in both directions */
202 FT_BitmapGlyph bglyph[16];
203 /** Outlined glyph bitmaps with 1/4 pixel precision in both directions */
204 FT_BitmapGlyph border_bglyph[16];
205 FT_BBox bbox;
206} Glyph;
207
208/** Global text metrics */
209typedef struct TextMetrics {
210 int offset_top64; ///< ascender amount of the first line (in 26.6 units)
211 int offset_bottom64; ///< descender amount of the last line (in 26.6 units)
212 int offset_left64; ///< maximum offset between the origin and
213 /// the leftmost pixel of the first glyph
214 /// of each line (in 26.6 units)
215 int offset_right64; ///< maximum offset between the origin and
216 /// the rightmost pixel of the last glyph
217 /// of each line (in 26.6 units)
218 int line_height64; ///< the font-defined line height
219 int width; ///< width of the longest line - ceil(width64/64)
220 int height; ///< total height of the text - ceil(height64/64)
221
222 int min_y64; ///< minimum value of bbox.yMin among glyphs (in 26.6 units)
223 int max_y64; ///< maximum value of bbox.yMax among glyphs (in 26.6 units)
224 int min_x64; ///< minimum value of bbox.xMin among glyphs (in 26.6 units)
225 int max_x64; ///< maximum value of bbox.xMax among glyphs (in 26.6 units)
226
227 // Position of the background box (without borders)
228 int rect_x; ///< x position of the box
229 int rect_y; ///< y position of the box
230} TextMetrics;
231
159typedef struct DrawTextContext {232typedef struct DrawTextContext {
160 const AVClass *class;233 const AVClass *class;
161 int exp_mode; ///< expansion mode to use for the text234 int exp_mode; ///< expansion mode to use for the text
@@ -169,11 +242,9 @@ typedef struct DrawTextContext { · 7 unmodified lines
169 uint8_t *fontcolor_expr; ///< fontcolor expression to evaluate242 uint8_t *fontcolor_expr; ///< fontcolor expression to evaluate
170 AVBPrint expanded_fontcolor; ///< used to contain the expanded fontcolor spec243 AVBPrint expanded_fontcolor; ///< used to contain the expanded fontcolor spec
171 int ft_load_flags; ///< flags used for loading fonts, see FT_LOAD_*244 int ft_load_flags; ///< flags used for loading fonts, see FT_LOAD_*
172 FT_Vector *positions; ///< positions for each element in the text
173 size_t nb_positions; ///< number of elements of positions array
174 char *textfile; ///< file with text to be drawn245 char *textfile; ///< file with text to be drawn
175 int x; ///< x position to start drawing text246 double x; ///< x position to start drawing text
176 int y; ///< y position to start drawing text247 double y; ///< y position to start drawing text
177 int max_glyph_w; ///< max glyph width248 int max_glyph_w; ///< max glyph width
178 int max_glyph_h; ///< max glyph height249 int max_glyph_h; ///< max glyph height
179 int shadowx, shadowy;250 int shadowx, shadowy;
@@ -186,7 +257,10 @@ typedef struct DrawTextContext { · 6 unmodified lines
186 int line_spacing; ///< lines spacing in pixels257 int line_spacing; ///< lines spacing in pixels
187 short int draw_box; ///< draw box around text - true or false258 short int draw_box; ///< draw box around text - true or false
188 int boxborderw; ///< box border width259 int boxborderw; ///< box border width
189 int use_kerning; ///< font kerning is used - true/false260 int bb_top; ///< the size of the top box border
261 int bb_right; ///< the size of the right box border
262 int bb_bottom; ///< the size of the bottom box border
263 int bb_left; ///< the size of the left box border
190 int tabsize; ///< tab size264 int tabsize; ///< tab size
191 int fix_bounds; ///< do we let it go out of frame bounds - t/f265 int fix_bounds; ///< do we let it go out of frame bounds - t/f
192 266
@@ -221,6 +295,12 @@ typedef struct DrawTextContext { · 28 unmodified lines
221 int text_shaping; ///< 1 to shape the text before drawing it295 int text_shaping; ///< 1 to shape the text before drawing it
222#endif296#endif
223 AVDictionary *metadata;297 AVDictionary *metadata;
298
299 TextLine *lines; ///< computed information about text lines
300 int line_count; ///< the number of text lines
301 uint32_t *tab_clusters; ///< the position of tab characters in the text
302 int tab_count; ///< the number of tab characters
303 int blank_advance64; ///< the size of the space character
224} DrawTextContext;304} DrawTextContext;
225 305
226#define OFFSET(x) offsetof(DrawTextContext, x)306#define OFFSET(x) offsetof(DrawTextContext, x)
@@ -236,7 +316,7 @@ static const AVOption drawtext_options[]= { · 9 unmodified lines
236 {"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS},316 {"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS},
237 {"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS},317 {"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS},
238 {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},318 {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
239 {"boxborderw", "set box borders width", OFFSET(boxborderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGS},319 {"boxborderw", "set box borders width", OFFSET(boxborderw), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS},
240 {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGS},320 {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGS},
241 {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS},321 {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS},
242 {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS},322 {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS},
@@ -305,19 +385,6 @@ static const struct ft_error { · 62 unmodified lines
305 385
306#define FT_ERRMSG(e) ft_errors[e].err_msg386#define FT_ERRMSG(e) ft_errors[e].err_msg
307 387
308typedef struct Glyph {
309 FT_Glyph glyph;
310 FT_Glyph border_glyph;
311 uint32_t code;
312 unsigned int fontsize;
313 FT_Bitmap bitmap; ///< array holding bitmaps of font
314 FT_Bitmap border_bitmap; ///< array holding bitmaps of font border
315 FT_BBox bbox;
316 int advance;
317 int bitmap_left;
318 int bitmap_top;
319} Glyph;
320
321static int glyph_cmp(const void *key, const void *b)388static int glyph_cmp(const void *key, const void *b)
322{389{
323 const Glyph *a = key, *bb = b;390 const Glyph *a = key, *bb = b;
@@ -329,77 +396,6 @@ static int glyph_cmp(const void *key, const void *b) · 5 unmodified lines
329 return FFDIFFSIGN((int64_t)a->fontsize, (int64_t)bb->fontsize);396 return FFDIFFSIGN((int64_t)a->fontsize, (int64_t)bb->fontsize);
330}397}
331 398
332/**
333 * Load glyphs corresponding to the UTF-32 codepoint code.
334 */
335static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
336{
337 DrawTextContext *s = ctx->priv;
338 FT_BitmapGlyph bitmapglyph;
339 Glyph *glyph;
340 struct AVTreeNode *node = NULL;
341 int ret;
342
343 /* load glyph into s->face->glyph */
344 if (FT_Load_Char(s->face, code, s->ft_load_flags))
345 return AVERROR(EINVAL);
346
347 glyph = av_mallocz(sizeof(*glyph));
348 if (!glyph) {
349 ret = AVERROR(ENOMEM);
350 goto error;
351 }
352 glyph->code = code;
353 glyph->fontsize = s->fontsize;
354
355 if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) {
356 ret = AVERROR(EINVAL);
357 goto error;
358 }
359 if (s->borderw) {
360 glyph->border_glyph = glyph->glyph;
361 if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0) ||
362 FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
363 ret = AVERROR_EXTERNAL;
364 goto error;
365 }
366 bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph;
367 glyph->border_bitmap = bitmapglyph->bitmap;
368 }
369 if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
370 ret = AVERROR_EXTERNAL;
371 goto error;
372 }
373 bitmapglyph = (FT_BitmapGlyph) glyph->glyph;
374
375 glyph->bitmap = bitmapglyph->bitmap;
376 glyph->bitmap_left = bitmapglyph->left;
377 glyph->bitmap_top = bitmapglyph->top;
378 glyph->advance = s->face->glyph->advance.x >> 6;
379
380 /* measure text height to calculate text_height (or the maximum text height) */
381 FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
382
383 /* cache the newly created glyph */
384 if (!(node = av_tree_node_alloc())) {
385 ret = AVERROR(ENOMEM);
386 goto error;
387 }
388 av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node);
389
390 if (glyph_ptr)
391 *glyph_ptr = glyph;
392 return 0;
393
394error:
395 if (glyph)
396 av_freep(&glyph->glyph);
397
398 av_freep(&glyph);
399 av_freep(&node);
400 return ret;
401}
402
403static av_cold int set_fontsize(AVFilterContext *ctx, unsigned int fontsize)399static av_cold int set_fontsize(AVFilterContext *ctx, unsigned int fontsize)
404{400{
405 int err;401 int err;
@@ -718,11 +714,114 @@ static enum AVFrameSideDataType text_source_string_parse(const char *text_source · 312 unmodified lines
718 }714 }
719}715}
720 716
717static inline int get_subpixel_idx(int shift_x64, int shift_y64)
718{
719 int idx = (shift_x64 >> 2) + (shift_y64 >> 4);
720 return idx;
721}
722
723// Loads and (optionally) renders a glyph
724static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code, int8_t shift_x64, int8_t shift_y64)
725{
726 DrawTextContext *s = ctx->priv;
727 Glyph dummy = { 0 };
728 Glyph *glyph;
729 FT_Vector shift;
730 struct AVTreeNode *node = NULL;
731 int ret = 0;
732
733 /* get glyph */
734 dummy.code = code;
735 dummy.fontsize = s->fontsize;
736 glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
737 if (!glyph) {
738 if (FT_Load_Glyph(s->face, code, s->ft_load_flags)) {
739 return AVERROR(EINVAL);
740 }
741 glyph = av_mallocz(sizeof(*glyph));
742 if (!glyph) {
743 ret = AVERROR(ENOMEM);
744 goto error;
745 }
746 glyph->code = code;
747 glyph->fontsize = s->fontsize;
748 if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) {
749 ret = AVERROR(EINVAL);
750 goto error;
751 }
752 if (s->borderw) {
753 glyph->border_glyph = glyph->glyph;
754 if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0)) {
755 ret = AVERROR_EXTERNAL;
756 goto error;
757 }
758 }
759 /* measure text height to calculate text_height (or the maximum text height) */
760 FT_Glyph_Get_CBox(glyph->glyph, FT_GLYPH_BBOX_SUBPIXELS, &glyph->bbox);
761
762 /* cache the newly created glyph */
763 if (!(node = av_tree_node_alloc())) {
764 ret = AVERROR(ENOMEM);
765 goto error;
766 }
767 av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node);
768 } else {
769 if (s->borderw && !glyph->border_glyph) {
770 glyph->border_glyph = glyph->glyph;
771 if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0)) {
772 ret = AVERROR_EXTERNAL;
773 goto error;
774 }
775 }
776 }
777
778 // Check if a bitmap is needed
779 if (shift_x64 >= 0 && shift_y64 >= 0) {
780 // Get the bitmap subpixel index (0 -> 15)
781 int idx = get_subpixel_idx(shift_x64, shift_y64);
782 shift.x = shift_x64;
783 shift.y = shift_y64;
784
785 if (!glyph->bglyph[idx]) {
786 FT_Glyph tmp_glyph = glyph->glyph;
787 if (FT_Glyph_To_Bitmap(&tmp_glyph, FT_RENDER_MODE_NORMAL, &shift, 0)) {
788 ret = AVERROR_EXTERNAL;
789 goto error;
790 }
791 glyph->bglyph[idx] = (FT_BitmapGlyph)tmp_glyph;
792 if (glyph->bglyph[idx]->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
793 av_log(ctx, AV_LOG_ERROR, "Monocromatic (1bpp) fonts are not supported.\n");
794 ret = AVERROR(EINVAL);
795 goto error;
796 }
797 }
798 if (s->borderw && !glyph->border_bglyph[idx]) {
799 FT_Glyph tmp_glyph = glyph->border_glyph;
800 if (FT_Glyph_To_Bitmap(&tmp_glyph, FT_RENDER_MODE_NORMAL, &shift, 0)) {
801 ret = AVERROR_EXTERNAL;
802 goto error;
803 }
804 glyph->border_bglyph[idx] = (FT_BitmapGlyph)tmp_glyph;
805 }
806 }
807 if (glyph_ptr) {
808 *glyph_ptr = glyph;
809 }
810 return 0;
811
812error:
813 if (glyph && glyph->glyph)
814 FT_Done_Glyph(glyph->glyph);
815
816 av_freep(&glyph);
817 av_freep(&node);
818 return ret;
819}
820
721static av_cold int init(AVFilterContext *ctx)821static av_cold int init(AVFilterContext *ctx)
722{822{
723 int err;823 int err;
724 DrawTextContext *s = ctx->priv;824 DrawTextContext *s = ctx->priv;
725 Glyph *glyph;
726 825
727 av_expr_free(s->fontsize_pexpr);826 av_expr_free(s->fontsize_pexpr);
728 s->fontsize_pexpr = NULL;827 s->fontsize_pexpr = NULL;
@@ -811,17 +910,8 @@ static av_cold int init(AVFilterContext *ctx) · 82 unmodified lines
811 FT_STROKER_LINEJOIN_ROUND, 0);910 FT_STROKER_LINEJOIN_ROUND, 0);
812 }911 }
813 912
814 s->use_kerning = FT_HAS_KERNING(s->face);
815
816 /* load the fallback glyph with code 0 */913 /* load the fallback glyph with code 0 */
817 load_glyph(ctx, NULL, 0);914 load_glyph(ctx, NULL, 0, 0, 0);
818
819 /* set the tabsize in pixels */
820 if ((err = load_glyph(ctx, &glyph, ' ')) < 0) {
821 av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n");
822 return err;
823 }
824 s->tabsize *= glyph->advance;
825 915
826 if (s->exp_mode == EXP_STRFTIME &&916 if (s->exp_mode == EXP_STRFTIME &&
827 (strchr(s->text, '%') || strchr(s->text, '\\')))917 (strchr(s->text, '%') || strchr(s->text, '\\')))
@@ -844,6 +934,14 @@ static int glyph_enu_free(void *opaque, void *elem) · 16 unmodified lines
844 934
845 FT_Done_Glyph(glyph->glyph);935 FT_Done_Glyph(glyph->glyph);
846 FT_Done_Glyph(glyph->border_glyph);936 FT_Done_Glyph(glyph->border_glyph);
937 for (int t = 0; t < 16; ++t) {
938 if (glyph->bglyph[t] != NULL) {
939 FT_Done_Glyph((FT_Glyph)glyph->bglyph[t]);
940 }
941 if (glyph->border_bglyph[t] != NULL) {
942 FT_Done_Glyph((FT_Glyph)glyph->border_bglyph[t]);
943 }
944 }
847 av_free(elem);945 av_free(elem);
848 return 0;946 return 0;
849}947}
@@ -859,9 +957,6 @@ static av_cold void uninit(AVFilterContext *ctx) · 9 unmodified lines
859 957
860 s->x_pexpr = s->y_pexpr = s->a_pexpr = s->fontsize_pexpr = NULL;958 s->x_pexpr = s->y_pexpr = s->a_pexpr = s->fontsize_pexpr = NULL;
861 959
862 av_freep(&s->positions);
863 s->nb_positions = 0;
864
865 av_tree_enumerate(s->glyphs, NULL, NULL, glyph_enu_free);960 av_tree_enumerate(s->glyphs, NULL, NULL, glyph_enu_free);
866 av_tree_destroy(s->glyphs);961 av_tree_destroy(s->glyphs);
867 s->glyphs = NULL;962 s->glyphs = NULL;
@@ -1325,91 +1420,283 @@ static int expand_text(AVFilterContext *ctx, char *text, AVBPrint *bp) · 457 unmodified lines
1325 return 0;1420 return 0;
1326}1421}
1327 1422
1423static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor)
1424{
1425 *color = incolor;
1426 color->rgba[3] = (color->rgba[3] * s->alpha) / 255;
1427 ff_draw_color(&s->dc, color, color->rgba);
1428}
1429
1430static void update_alpha(DrawTextContext *s)
1431{
1432 double alpha = av_expr_eval(s->a_pexpr, s->var_values, &s->prng);
1433
1434 if (isnan(alpha))
1435 return;
1436
1437 if (alpha >= 1.0)
1438 s->alpha = 255;
1439 else if (alpha <= 0)
1440 s->alpha = 0;
1441 else
1442 s->alpha = 256 * alpha;
1443}
1444
1328static int draw_glyphs(DrawTextContext *s, AVFrame *frame,1445static int draw_glyphs(DrawTextContext *s, AVFrame *frame,
1329 int width, int height,
1330 FFDrawColor *color,1446 FFDrawColor *color,
1447 TextMetrics *metrics,
1331 int x, int y, int borderw)1448 int x, int y, int borderw)
1332{1449{
1333 char *text = s->expanded_text.str;1450 int g, l, x1, y1, w1, h1, idx;
1334 uint32_t code = 0;1451 int dx = 0, dy = 0, pdx = 0;
1335 int i, x1, y1;1452 GlyphInfo *info;
1336 uint8_t *p;1453 Glyph dummy = { 0 }, *glyph;
1337 Glyph *glyph = NULL;1454 FT_Bitmap bitmap;
1338 1455 FT_BitmapGlyph b_glyph;
1339 for (i = 0, p = text; *p; i++) {1456 int clip_x = 0, clip_y = 0;
1340 FT_Bitmap bitmap;1457
1341 Glyph dummy = { 0 };1458 clip_x = FFMIN(metrics->rect_x + metrics->width + s->bb_right, frame->width);
1342 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid;);1459 clip_y = FFMIN(metrics->rect_y + metrics->height + s->bb_bottom, frame->height);
1343continue_on_invalid:1460
1461 for (l = 0; l < s->line_count; ++l) {
1462 TextLine *line = &s->lines[l];
1463 for (g = 0; g < line->hb_data.glyph_count; ++g) {
1464 info = &line->glyphs[g];
1465 dummy.fontsize = s->fontsize;
1466 dummy.code = info->code;
1467 glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
1468 if (!glyph) {
1469 return AVERROR(EINVAL);
1470 }
1344 1471
1345 /* skip new line chars, just go to new line */1472 idx = get_subpixel_idx(info->shift_x64, info->shift_y64);
1346 if (code == '\n' || code == '\r' || code == '\t')1473 b_glyph = borderw ? glyph->border_bglyph[idx] : glyph->bglyph[idx];
1347 continue;1474 bitmap = b_glyph->bitmap;
1475 x1 = x + info->x + b_glyph->left;
1476 y1 = y + info->y - b_glyph->top;
1477 w1 = bitmap.width;
1478 h1 = bitmap.rows;
1479
1480 // Offset of the glyph's bitmap in the visible region
1481 dx = dy = 0;
1482 if (x1 < metrics->rect_x - s->bb_left) {
1483 dx = metrics->rect_x - s->bb_left - x1;
1484 x1 = metrics->rect_x - s->bb_left;
1485 }
1486 if (y1 < metrics->rect_y - s->bb_top) {
1487 dy = metrics->rect_y - s->bb_top - y1;
1488 y1 = metrics->rect_y - s->bb_top;
1489 }
1348 1490
1349 dummy.code = code;1491 // check if the glyph is empty or out of the clipping region
1350 dummy.fontsize = s->fontsize;1492 if (dx >= w1 || dy >= h1 || x1 >= clip_x || y1 >= clip_y) {
1351 glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);1493 continue;
1494 }
1352 1495
1353 bitmap = borderw ? glyph->border_bitmap : glyph->bitmap;1496 pdx = dx + dy * bitmap.pitch;
1497 w1 = FFMIN(clip_x - x1, w1 - dx);
1498 h1 = FFMIN(clip_y - y1, h1 - dy);
1354 1499
1355 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&1500 ff_blend_mask(&s->dc, color, frame->data, frame->linesize, clip_x, clip_y,
1356 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)1501 bitmap.buffer + pdx, bitmap.pitch, w1, h1, 3, 0, x1, y1);
1357 return AVERROR(EINVAL);1502 }
1503 }
1358 1504
1359 x1 = s->positions[i].x+s->x+x - borderw;1505 return 0;
1360 y1 = s->positions[i].y+s->y+y - borderw;1506}
1361 1507
1362 ff_blend_mask(&s->dc, color,1508// Shapes a line of text using libharfbuzz
1363 frame->data, frame->linesize, width, height,1509static int shape_text_hb(DrawTextContext *s, HarfbuzzData* hb, const char* text, int textLen)
1364 bitmap.buffer, bitmap.pitch,1510{
1365 bitmap.width, bitmap.rows,1511 hb->buf = hb_buffer_create();
1366 bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,1512 if(!hb_buffer_allocation_successful(hb->buf)) {
1367 0, x1, y1);1513 return AVERROR(ENOMEM);
1368 }1514 }
1515 hb_buffer_set_direction(hb->buf, HB_DIRECTION_LTR);
1516 hb_buffer_set_script(hb->buf, HB_SCRIPT_LATIN);
1517 hb_buffer_set_language(hb->buf, hb_language_from_string("en", -1));
1518 hb_buffer_guess_segment_properties(hb->buf);
1519 hb->font = hb_ft_font_create(s->face, NULL);
1520 if(hb->font == NULL) {
1521 return AVERROR(ENOMEM);
1522 }
1523 hb_ft_font_set_funcs(hb->font);
1524 hb_buffer_add_utf8(hb->buf, text, textLen, 0, -1);
1525 hb_shape(hb->font, hb->buf, NULL, 0);
1526 hb->glyph_info = hb_buffer_get_glyph_infos(hb->buf, &hb->glyph_count);
1527 hb->glyph_pos = hb_buffer_get_glyph_positions(hb->buf, &hb->glyph_count);
1369 1528
1370 return 0;1529 return 0;
1371}1530}
1372 1531
1373 1532static void hb_destroy(HarfbuzzData *hb)
1374static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor)
1375{1533{
1376 *color = incolor;1534 hb_buffer_destroy(hb->buf);
1377 color->rgba[3] = (color->rgba[3] * s->alpha) / 255;1535 hb_font_destroy(hb->font);
1378 ff_draw_color(&s->dc, color, color->rgba);1536 hb->buf = NULL;
1537 hb->font = NULL;
1538 hb->glyph_info = NULL;
1539 hb->glyph_pos = NULL;
1379}1540}
1380 1541
1381static void update_alpha(DrawTextContext *s)1542static int measure_text(AVFilterContext *ctx, TextMetrics *metrics)
1382{1543{
1383 double alpha = av_expr_eval(s->a_pexpr, s->var_values, &s->prng);1544 DrawTextContext *s = ctx->priv;
1545 char *text = s->expanded_text.str;
1546 char *textdup = NULL, *start = NULL;
1547 int num_chars = 0;
1548 int width64 = 0, w64 = 0;
1549 int cur_min_y64 = 0, first_max_y64 = -32000;
1550 int first_min_x64 = 32000, last_max_x64 = -32000;
1551 int min_y64 = 32000, max_y64 = -32000, min_x64 = 32000, max_x64 = -32000;
1552 int line_count = 0;
1553 uint32_t code = 0;
1554 Glyph *glyph = NULL;
1555 int height64;
1556
1557 int i, tab_idx = 0, last_tab_idx = 0, line_offset = 0;
1558 char* p;
1559 int ret = 0;
1560
1561 // Count the lines and the tab characters
1562 s->tab_count = 0;
1563 for (i = 0, p = text; 1; i++) {
1564 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed;);
1565continue_on_failed:
1566 if (is_newline(code) || code == 0) {
1567 ++line_count;
1568 if (code == 0) {
1569 break;
1570 }
1571 } else if (code == '\t') {
1572 ++s->tab_count;
1573 }
1574 }
1384 1575
1385 if (isnan(alpha))1576 // Evaluate the width of the space character if needed to replace tabs
1386 return;1577 if (s->tab_count > 0 && !s->blank_advance64) {
1578 HarfbuzzData hb_data;
1579 ret = shape_text_hb(s, &hb_data, " ", 1);
1580 if(ret != 0) {
1581 goto done;
1582 }
1583 s->blank_advance64 = hb_data.glyph_pos[0].x_advance;
1584 hb_destroy(&hb_data);
1585 }
1387 1586
1388 if (alpha >= 1.0)1587 s->line_count = line_count;
1389 s->alpha = 255;1588 s->lines = av_mallocz(line_count * sizeof(TextLine));
1390 else if (alpha <= 0)1589 s->tab_clusters = av_mallocz(s->tab_count * sizeof(uint32_t));
1391 s->alpha = 0;1590 for (i = 0; i < s->tab_count; ++i) {
1392 else1591 s->tab_clusters[i] = -1;
1393 s->alpha = 256 * alpha;1592 }
1593
1594 start = textdup = av_strdup(text);
1595 if (textdup == NULL) {
1596 ret = AVERROR(ENOMEM);
1597 goto done;
1598 }
1599 line_count = 0;
1600 for (i = 0, p = textdup; 1; i++) {
1601 if (*p == '\t') {
1602 s->tab_clusters[tab_idx++] = i;
1603 *p = ' ';
1604 }
1605 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed2;);
1606continue_on_failed2:
1607 if (is_newline(code) || code == 0) {
1608 TextLine *cur_line = &s->lines[line_count];
1609 HarfbuzzData *hb = &cur_line->hb_data;
1610 cur_line->cluster_offset = line_offset;
1611 ret = shape_text_hb(s, hb, start, num_chars);
1612 if (ret != 0) {
1613 goto done;
1614 }
1615 w64 = 0;
1616 cur_min_y64 = 32000;
1617 for (int t = 0; t < hb->glyph_count; ++t) {
1618 uint8_t is_tab = last_tab_idx < s->tab_count &&
1619 hb->glyph_info[t].cluster == s->tab_clusters[last_tab_idx] - line_offset;
1620 if (is_tab) {
1621 ++last_tab_idx;
1622 }
1623 ret = load_glyph(ctx, &glyph, hb->glyph_info[t].codepoint, -1, -1);
1624 if (ret != 0) {
1625 goto done;
1626 }
1627 if (line_count == 0) {
1628 first_max_y64 = FFMAX(glyph->bbox.yMax, first_max_y64);
1629 }
1630 if (t == 0) {
1631 cur_line->offset_left64 = glyph->bbox.xMin;
1632 first_min_x64 = FFMIN(glyph->bbox.xMin, first_min_x64);
1633 }
1634 if (t == hb->glyph_count - 1) {
1635 w64 += glyph->bbox.xMax;
1636 last_max_x64 = FFMAX(glyph->bbox.xMax, last_max_x64);
1637 cur_line->offset_right64 = glyph->bbox.xMax;
1638 } else {
1639 if (is_tab) {
1640 int size = s->blank_advance64 * s->tabsize;
1641 w64 = (w64 / size + 1) * size;
1642 } else {
1643 w64 += hb->glyph_pos[t].x_advance;
1644 }
1645 }
1646 cur_min_y64 = FFMIN(glyph->bbox.yMin, cur_min_y64);
1647 min_y64 = FFMIN(glyph->bbox.yMin, min_y64);
1648 max_y64 = FFMAX(glyph->bbox.yMax, max_y64);
1649 min_x64 = FFMIN(glyph->bbox.xMin, min_x64);
1650 max_x64 = FFMAX(glyph->bbox.xMax, max_x64);
1651 }
1652
1653 cur_line->width64 = w64;
1654
1655 av_log(s, AV_LOG_DEBUG, " Line: %d -- glyphs count: %d - width64: %d - offset_left64: %d - offset_right64: %d)\n",
1656 line_count, hb->glyph_count, cur_line->width64, cur_line->offset_left64, cur_line->offset_right64);
1657
1658 if (w64 > width64) {
1659 width64 = w64;
1660 }
1661 num_chars = -1;
1662 start = p;
1663 ++line_count;
1664 line_offset = i + 1;
1665 }
1666
1667 if (code == 0) break;
1668 ++num_chars;
1669 }
1670
1671 metrics->line_height64 = s->face->size->metrics.height;
1672
1673 metrics->width = POS_CEIL(width64, 64);
1674 height64 = (metrics->line_height64 + s->line_spacing * 64) *
1675 (FFMAX(0, line_count - 1)) + first_max_y64 - cur_min_y64;
1676 metrics->height = POS_CEIL(height64, 64);
1677
1678 metrics->offset_top64 = first_max_y64;
1679 metrics->offset_right64 = last_max_x64;
1680 metrics->offset_bottom64 = cur_min_y64;
1681 metrics->offset_left64 = first_min_x64;
1682 metrics->min_x64 = min_x64;
1683 metrics->min_y64 = min_y64;
1684 metrics->max_x64 = max_x64;
1685 metrics->max_y64 = max_y64;
1686
1687done:
1688 av_free(textdup);
1689 return ret;
1394}1690}
1395 1691
1396static int draw_text(AVFilterContext *ctx, AVFrame *frame,1692static int draw_text(AVFilterContext *ctx, AVFrame *frame)
1397 int width, int height)
1398{1693{
1399 DrawTextContext *s = ctx->priv;1694 DrawTextContext *s = ctx->priv;
1400 AVFilterLink *inlink = ctx->inputs[0];1695 AVFilterLink *inlink = ctx->inputs[0];
1401 1696 int x = 0, y = 0, ret;
1402 uint32_t code = 0, prev_code = 0;1697 int shift_x64, shift_y64;
1403 int x = 0, y = 0, i = 0, ret;1698 int x64, y64;
1404 int max_text_line_w = 0, len;1699 Glyph *glyph = NULL;
1405 int box_w, box_h;
1406 char *text;
1407 uint8_t *p;
1408 int y_min = 32000, y_max = -32000;
1409 int x_min = 32000, x_max = -32000;
1410 FT_Vector delta;
1411 Glyph *glyph = NULL, *prev_glyph = NULL;
1412 Glyph dummy = { 0 };
1413 1700
1414 time_t now = time(0);1701 time_t now = time(0);
1415 struct tm ltime;1702 struct tm ltime;
@@ -1420,6 +1707,14 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, · 4 unmodified lines
1420 FFDrawColor bordercolor;1707 FFDrawColor bordercolor;
1421 FFDrawColor boxcolor;1708 FFDrawColor boxcolor;
1422 1709
1710 int width = frame->width;
1711 int height = frame->height;
1712 int rec_x = 0, rec_y = 0, rec_width = 0, rec_height = 0;
1713 int is_outside = 0;
1714 int last_tab_idx = 0;
1715
1716 TextMetrics metrics;
1717
1423 av_bprint_clear(bp);1718 av_bprint_clear(bp);
1424 1719
1425 if (s->basetime != AV_NOPTS_VALUE)1720 if (s->basetime != AV_NOPTS_VALUE)
@@ -1448,13 +1743,6 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, · 22 unmodified lines
1448 1743
1449 if (!av_bprint_is_complete(bp))1744 if (!av_bprint_is_complete(bp))
1450 return AVERROR(ENOMEM);1745 return AVERROR(ENOMEM);
1451 text = s->expanded_text.str;
1452 if ((len = s->expanded_text.len) > s->nb_positions) {
1453 if (!(s->positions =
1454 av_realloc(s->positions, len*sizeof(*s->positions))))
1455 return AVERROR(ENOMEM);
1456 s->nb_positions = len;
1457 }
1458 1746
1459 if (s->fontcolor_expr[0]) {1747 if (s->fontcolor_expr[0]) {
1460 /* If expression is set, evaluate and replace the static value */1748 /* If expression is set, evaluate and replace the static value */
@@ -1470,85 +1758,26 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, · 9 unmodified lines
1470 ff_draw_color(&s->dc, &s->fontcolor, s->fontcolor.rgba);1758 ff_draw_color(&s->dc, &s->fontcolor, s->fontcolor.rgba);
1471 }1759 }
1472 1760
1473 x = 0;1761 if ((ret = update_fontsize(ctx)) < 0) {
1474 y = 0;
1475
1476 if ((ret = update_fontsize(ctx)) < 0)
1477 return ret;1762 return ret;
1478
1479 /* load and cache glyphs */
1480 for (i = 0, p = text; *p; i++) {
1481 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid;);
1482continue_on_invalid:
1483
1484 /* get glyph */
1485 dummy.code = code;
1486 dummy.fontsize = s->fontsize;
1487 glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
1488 if (!glyph) {
1489 ret = load_glyph(ctx, &glyph, code);
1490 if (ret < 0)
1491 return ret;
1492 }
1493
1494 y_min = FFMIN(glyph->bbox.yMin, y_min);
1495 y_max = FFMAX(glyph->bbox.yMax, y_max);
1496 x_min = FFMIN(glyph->bbox.xMin, x_min);
1497 x_max = FFMAX(glyph->bbox.xMax, x_max);
1498 }1763 }
1499 s->max_glyph_h = y_max - y_min;
1500 s->max_glyph_w = x_max - x_min;
1501
1502 /* compute and save position for each glyph */
1503 glyph = NULL;
1504 for (i = 0, p = text; *p; i++) {
1505 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid2;);
1506continue_on_invalid2:
1507
1508 /* skip the \n in the sequence \r\n */
1509 if (prev_code == '\r' && code == '\n')
1510 continue;
1511
1512 prev_code = code;
1513 if (is_newline(code)) {
1514
1515 max_text_line_w = FFMAX(max_text_line_w, x);
1516 y += s->max_glyph_h + s->line_spacing;
1517 x = 0;
1518 continue;
1519 }
1520
1521 /* get glyph */
1522 prev_glyph = glyph;
1523 dummy.code = code;
1524 dummy.fontsize = s->fontsize;
1525 glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
1526
1527 /* kerning */
1528 if (s->use_kerning && prev_glyph && glyph->code) {
1529 FT_Get_Kerning(s->face, prev_glyph->code, glyph->code,
1530 ft_kerning_default, &delta);
1531 x += delta.x >> 6;
1532 }
1533 1764
1534 /* save position */1765 if ((ret = measure_text(ctx, &metrics)) < 0) {
1535 s->positions[i].x = x + glyph->bitmap_left;1766 return ret;
1536 s->positions[i].y = y - glyph->bitmap_top + y_max;
1537 if (code == '\t') x = (x / s->tabsize + 1)*s->tabsize;
1538 else x += glyph->advance;
1539 }1767 }
1540 1768
1541 max_text_line_w = FFMAX(x, max_text_line_w);1769 s->max_glyph_h = POS_CEIL(metrics.max_y64 - metrics.min_y64, 64);
1770 s->max_glyph_w = POS_CEIL(metrics.max_x64 - metrics.min_x64, 64);
1542 1771
1543 s->var_values[VAR_TW] = s->var_values[VAR_TEXT_W] = max_text_line_w;1772 s->var_values[VAR_TW] = s->var_values[VAR_TEXT_W] = metrics.width;
1544 s->var_values[VAR_TH] = s->var_values[VAR_TEXT_H] = y + s->max_glyph_h;1773 s->var_values[VAR_TH] = s->var_values[VAR_TEXT_H] = metrics.height;
1545 1774
1546 s->var_values[VAR_MAX_GLYPH_W] = s->max_glyph_w;1775 s->var_values[VAR_MAX_GLYPH_W] = s->max_glyph_w;
1547 s->var_values[VAR_MAX_GLYPH_H] = s->max_glyph_h;1776 s->var_values[VAR_MAX_GLYPH_H] = s->max_glyph_h;
1548 s->var_values[VAR_MAX_GLYPH_A] = s->var_values[VAR_ASCENT ] = y_max;1777 s->var_values[VAR_MAX_GLYPH_A] = s->var_values[VAR_ASCENT] = POS_CEIL(metrics.max_y64, 64);
1549 s->var_values[VAR_MAX_GLYPH_D] = s->var_values[VAR_DESCENT] = y_min;1778 s->var_values[VAR_MAX_GLYPH_D] = s->var_values[VAR_DESCENT] = POS_CEIL(metrics.min_y64, 64);
1550 1779
1551 s->var_values[VAR_LINE_H] = s->var_values[VAR_LH] = s->max_glyph_h;1780 s->var_values[VAR_LINE_H] = s->var_values[VAR_LH] = metrics.line_height64 / 64.;
1552 1781
1553 if (s->text_source == AV_FRAME_DATA_DETECTION_BBOXES) {1782 if (s->text_source == AV_FRAME_DATA_DETECTION_BBOXES) {
1554 s->var_values[VAR_X] = s->x;1783 s->var_values[VAR_X] = s->x;
@@ -1566,56 +1795,139 @@ continue_on_invalid2: · 11 unmodified lines
1566 update_color_with_alpha(s, &bordercolor, s->bordercolor);1795 update_color_with_alpha(s, &bordercolor, s->bordercolor);
1567 update_color_with_alpha(s, &boxcolor , s->boxcolor );1796 update_color_with_alpha(s, &boxcolor , s->boxcolor );
1568 1797
1569 box_w = max_text_line_w;1798 if (s->draw_box && s->boxborderw) {
1570 box_h = y + s->max_glyph_h;1799 s->bb_top = s->bb_right = s->bb_bottom = s->bb_left = s->boxborderw;
1800 } else {
1801 s->bb_top = s->bb_right = s->bb_bottom = s->bb_left = 0;
1802 }
1571 1803
1572 if (s->fix_bounds) {1804 if (s->fix_bounds) {
1573
1574 /* calculate footprint of text effects */1805 /* calculate footprint of text effects */
1575 int boxoffset = s->draw_box ? FFMAX(s->boxborderw, 0) : 0;
1576 int borderoffset = s->borderw ? FFMAX(s->borderw, 0) : 0;1806 int borderoffset = s->borderw ? FFMAX(s->borderw, 0) : 0;
1577 1807
1578 int offsetleft = FFMAX3(boxoffset, borderoffset,1808 int offsetleft = FFMAX3(FFMAX(s->bb_left, 0), borderoffset,
1579 (s->shadowx < 0 ? FFABS(s->shadowx) : 0));1809 (s->shadowx < 0 ? FFABS(s->shadowx) : 0));
1580 int offsettop = FFMAX3(boxoffset, borderoffset,1810 int offsettop = FFMAX3(FFMAX(s->bb_top, 0), borderoffset,
1581 (s->shadowy < 0 ? FFABS(s->shadowy) : 0));1811 (s->shadowy < 0 ? FFABS(s->shadowy) : 0));
1582 1812 int offsetright = FFMAX3(FFMAX(s->bb_right, 0), borderoffset,
1583 int offsetright = FFMAX3(boxoffset, borderoffset,
1584 (s->shadowx > 0 ? s->shadowx : 0));1813 (s->shadowx > 0 ? s->shadowx : 0));
1585 int offsetbottom = FFMAX3(boxoffset, borderoffset,1814 int offsetbottom = FFMAX3(FFMAX(s->bb_bottom, 0), borderoffset,
1586 (s->shadowy > 0 ? s->shadowy : 0));1815 (s->shadowy > 0 ? s->shadowy : 0));
1587 1816
1588
1589 if (s->x - offsetleft < 0) s->x = offsetleft;1817 if (s->x - offsetleft < 0) s->x = offsetleft;
1590 if (s->y - offsettop < 0) s->y = offsettop;1818 if (s->y - offsettop < 0) s->y = offsettop;
1591 1819
1592 if (s->x + box_w + offsetright > width)1820 if (s->x + metrics.width + offsetright > width)
1593 s->x = FFMAX(width - box_w - offsetright, 0);1821 s->x = FFMAX(width - metrics.width - offsetright, 0);
1594 if (s->y + box_h + offsetbottom > height)1822 if (s->y + metrics.height + offsetbottom > height)
1595 s->y = FFMAX(height - box_h - offsetbottom, 0);1823 s->y = FFMAX(height - metrics.height - offsetbottom, 0);
1596 }1824 }
1597 1825
1598 /* draw box */1826 x = 0;
1599 if (s->draw_box)1827 y = 0;
1600 ff_blend_rectangle(&s->dc, &boxcolor,1828 x64 = (int)(s->x * 64.);
1601 frame->data, frame->linesize, width, height,1829 y64 = (int)(s->y * 64. + metrics.offset_top64);
1602 s->x - s->boxborderw, s->y - s->boxborderw,1830
1603 box_w + s->boxborderw * 2, box_h + s->boxborderw * 2);1831 for (int l = 0; l < s->line_count; ++l) {
1832 TextLine *line = &s->lines[l];
1833 HarfbuzzData *hb = &line->hb_data;
1834 line->glyphs = av_mallocz(hb->glyph_count * sizeof(GlyphInfo));
1835
1836 for (int t = 0; t < hb->glyph_count; ++t) {
1837 GlyphInfo *g_info = &line->glyphs[t];
1838 uint8_t is_tab = last_tab_idx < s->tab_count &&
1839 hb->glyph_info[t].cluster == s->tab_clusters[last_tab_idx] - line->cluster_offset;
1840 int true_x, true_y;
1841 if (is_tab) {
1842 ++last_tab_idx;
1843 }
1844 true_x = x + hb->glyph_pos[t].x_offset;
1845 true_y = y + hb->glyph_pos[t].y_offset;
1846 shift_x64 = (((x64 + true_x) >> 4) & 0b0011) << 4;
1847 shift_y64 = ((4 - (((y64 + true_y) >> 4) & 0b0011)) & 0b0011) << 4;
1848
1849 ret = load_glyph(ctx, &glyph, hb->glyph_info[t].codepoint, shift_x64, shift_y64);
1850 if (ret != 0) {
1851 return ret;
1852 }
1853 g_info->code = hb->glyph_info[t].codepoint;
1854 g_info->x = (x64 + true_x) >> 6;
1855 g_info->y = ((y64 + true_y) >> 6) + (shift_y64 > 0 ? 1 : 0);
1856 g_info->shift_x64 = shift_x64;
1857 g_info->shift_y64 = shift_y64;
1858
1859 if (!is_tab) {
1860 x += hb->glyph_pos[t].x_advance;
1861 } else {
1862 int size = s->blank_advance64 * s->tabsize;
1863 x = (x / size + 1) * size;
1864 }
1865 y += hb->glyph_pos[t].y_advance;
1866 }
1604 1867
1605 if (s->shadowx || s->shadowy) {1868 y += metrics.line_height64 + s->line_spacing * 64;
1606 if ((ret = draw_glyphs(s, frame, width, height,1869 x = 0;
1607 &shadowcolor, s->shadowx, s->shadowy, 0)) < 0)
1608 return ret;
1609 }1870 }
1610 1871
1611 if (s->borderw) {1872 metrics.rect_x = s->x;
1612 if ((ret = draw_glyphs(s, frame, width, height,1873 metrics.rect_y = s->y;
1613 &bordercolor, 0, 0, s->borderw)) < 0)1874
1875 if (!s->draw_box) {
1876 // Create a border for the clipping region to take into account subpixel
1877 // errors in text measurement and effects.
1878 int borderoffset = s->borderw ? FFMAX(s->borderw, 0) : 0;
1879 s->bb_left = borderoffset + (s->shadowx < 0 ? FFABS(s->shadowx) : 0) + 1;
1880 s->bb_top = borderoffset + (s->shadowy < 0 ? FFABS(s->shadowy) : 0) + 1;
1881 s->bb_right = borderoffset + (s->shadowx > 0 ? s->shadowx : 0) + 1;
1882 s->bb_bottom = borderoffset + (s->shadowy > 0 ? s->shadowy : 0) + 1;
1883 }
1884
1885 /* Check if the whole box is out of the frame */
1886 is_outside = metrics.rect_x - s->bb_left >= width ||
1887 metrics.rect_y - s->bb_top >= height ||
1888 metrics.rect_x + metrics.width + s->bb_right <= 0 ||
1889 metrics.rect_y + metrics.height + s->bb_bottom <= 0;
1890
1891 if (!is_outside) {
1892 /* draw box */
1893 if (s->draw_box) {
1894 rec_x = metrics.rect_x - s->bb_left;
1895 rec_y = metrics.rect_y - s->bb_top;
1896 rec_width = metrics.width + s->bb_right + s->bb_left;
1897 rec_height = metrics.height + s->bb_bottom + s->bb_top;
1898 ff_blend_rectangle(&s->dc, &boxcolor,
1899 frame->data, frame->linesize, width, height,
1900 rec_x, rec_y, rec_width, rec_height);
1901 }
1902
1903 if (s->shadowx || s->shadowy) {
1904 if ((ret = draw_glyphs(s, frame, &shadowcolor, &metrics,
1905 s->shadowx, s->shadowy, s->borderw)) < 0) {
1906 return ret;
1907 }
1908 }
1909
1910 if (s->borderw) {
1911 if ((ret = draw_glyphs(s, frame, &bordercolor, &metrics,
1912 0, 0, s->borderw)) < 0) {
1913 return ret;
1914 }
1915 }
1916
1917 if ((ret = draw_glyphs(s, frame, &fontcolor, &metrics, 0,
1918 0, 0)) < 0) {
1614 return ret;1919 return ret;
1920 }
1615 }1921 }
1616 if ((ret = draw_glyphs(s, frame, width, height,1922
1617 &fontcolor, 0, 0, 0)) < 0)1923 // FREE data structures
1618 return ret;1924 for (int l = 0; l < s->line_count; ++l) {
1925 TextLine *line = &s->lines[l];
1926 av_freep(&line->glyphs);
1927 hb_destroy(&line->hb_data);
1928 }
1929 av_freep(&s->lines);
1930 av_freep(&s->tab_clusters);
1619 1931
1620 return 0;1932 return 0;
1621}1933}
@@ -1691,14 +2003,9 @@ FF_ENABLE_DEPRECATION_WARNINGS · 69 unmodified lines
1691 s->x = bbox->x;2003 s->x = bbox->x;
1692 s->y = bbox->y - s->fontsize;2004 s->y = bbox->y - s->fontsize;
1693 }2005 }
1694 draw_text(ctx, frame, frame->width, frame->height);2006 draw_text(ctx, frame);
1695 }2007 }
1696 2008
1697 av_log(ctx, AV_LOG_DEBUG, "n:%d t:%f text_w:%d text_h:%d x:%d y:%d\n",
1698 (int)s->var_values[VAR_N], s->var_values[VAR_T],
1699 (int)s->var_values[VAR_TEXT_W], (int)s->var_values[VAR_TEXT_H],
1700 s->x, s->y);
1701
1702 return ff_filter_frame(outlink, frame);2009 return ff_filter_frame(outlink, frame);
1703}2010}
1704 2011

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t