This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 46c9584dc5b63229b16e13b86c40610f5af11886
Author:     Ayose <[email protected]>
AuthorDate: Fri Dec 5 14:19:05 2025 +0000
Commit:     Marton Balint <[email protected]>
CommitDate: Sun Jan 4 13:42:20 2026 +0000

    avfilter/vf_drawvg: support color expressions as setvar/call arguments.
    
    The arguments for `setvar` and `call` commands can be colors (like 
`#rrggbb`).
    This replaces the previous trick of using `0xRRGGBBAA` values to use colors 
as
    procedure arguments.
    
    The parser stores colors as the value expected by Cairo (a `double[4]`). 
This
    array is allocated on the heap so the size of the union in `VGSArgument` is
    not increased (i.e. it is still 8 bytes, instead of 32).
    
    Signed-off-by: Ayose <[email protected]>
---
 libavfilter/vf_drawvg.c                  | 159 +++++++++++++++++++++++--------
 tests/ref/fate/filter-drawvg-interpreter |  10 ++
 tests/ref/lavf/drawvg.all                |  12 +++
 3 files changed, 140 insertions(+), 41 deletions(-)

diff --git a/libavfilter/vf_drawvg.c b/libavfilter/vf_drawvg.c
index 5ef04ca616..f89605185f 100644
--- a/libavfilter/vf_drawvg.c
+++ b/libavfilter/vf_drawvg.c
@@ -26,6 +26,7 @@
  */
 
 #include <cairo.h>
+#include <stdbool.h>
 
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
@@ -220,6 +221,7 @@ struct VGSParameter {
         PARAM_END,
         PARAM_MAY_REPEAT,
         PARAM_NUMERIC,
+        PARAM_NUMERIC_COLOR,
         PARAM_NUMERIC_METADATA,
         PARAM_PROC_ARGS,
         PARAM_PROC_NAME,
@@ -328,7 +330,7 @@ static const struct VGSCommandSpec vgs_commands[] = {
     { "setlinejoin",    CMD_SET_LINE_JOIN,    L(C(vgs_consts_line_join)) },
     { "setlinewidth",   CMD_SET_LINE_WIDTH,   L(N) },
     { "setrgba",        CMD_SET_RGBA,         L(N, N, N, N) },
-    { "setvar",         CMD_SET_VAR,          L(V, N) },
+    { "setvar",         CMD_SET_VAR,          L(V, { PARAM_NUMERIC_COLOR }) },
     { "stroke",         CMD_STROKE,           NONE },
     { "t",              CMD_T_CURVE_TO_REL,   R(N, N) },
     { "translate",      CMD_TRANSLATE,        L(N, N) },
@@ -406,6 +408,22 @@ static int vgs_cmd_change_path(enum VGSCommand cmd) {
     }
 }
 
+
+/// Colors in cairo are defined by 4 values, between 0 and 1. Computed colors
+/// (either by #RRGGBB expressions, or by commands like `defhsla`) are stored
+/// in the values that will be sent to Cairo.
+typedef double cairo_color[4];
+
+static av_always_inline void color_copy(cairo_color *dest, const cairo_color 
*src) {
+    memcpy(dest, src, sizeof(cairo_color));
+}
+
+static av_always_inline void color_reset(cairo_color *const dest) {
+    for (int i = 0; i < FF_ARRAY_ELEMS(*dest); i++)
+        (*dest)[i] = NAN;
+}
+
+
 /*
  * == VGS Parser ==
  *
@@ -631,7 +649,6 @@ next_token:
 struct VGSArgument {
     enum {
         ARG_COLOR = 1,
-        ARG_COLOR_VAR,
         ARG_CONST,
         ARG_EXPR,
         ARG_LITERAL,
@@ -642,7 +659,7 @@ struct VGSArgument {
     } type;
 
     union {
-        uint8_t color[4];
+        cairo_color *color;
         int constant;
         AVExpr *expr;
         double literal;
@@ -686,6 +703,10 @@ static void vgs_statement_free(struct VGSStatement *stm) {
         struct VGSArgument *arg = &stm->args[j];
 
         switch (arg->type) {
+        case ARG_COLOR:
+            av_freep(&arg->color);
+            break;
+
         case ARG_EXPR:
             av_expr_free(arg->expr);
             break;
@@ -720,6 +741,32 @@ static void vgs_free(struct VGSProgram *program) {
     }
 }
 
+static int vgs_parse_color(
+    void *log_ctx,
+    struct VGSArgument *arg,
+    const struct VGSParser *parser,
+    const struct VGSParserToken *token
+) {
+    uint8_t color[4];
+
+    const int ret = av_parse_color(color, token->lexeme, token->length, 
log_ctx);
+    if (ret != 0) {
+        vgs_log_invalid_token(log_ctx, parser, token, "Expected color.");
+        return ret;
+    }
+
+    arg->type = ARG_COLOR;
+    arg->color = av_malloc(sizeof(cairo_color));
+
+    if (arg->color == NULL)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < FF_ARRAY_ELEMS(*arg->color); i++)
+        (*arg->color)[i] = (double)color[i] / 255.0;
+
+    return 0;
+}
+
 /// Consume the next argument as a numeric value, and store it in `arg`.
 ///
 /// Return `0` on success, and a negative `AVERROR` code on failure.
@@ -727,7 +774,8 @@ static int vgs_parse_numeric_argument(
     void *log_ctx,
     struct VGSParser *parser,
     struct VGSArgument *arg,
-    int metadata
+    int metadata,
+    bool accept_colors
 ) {
     int ret;
     char stack_buf[64];
@@ -783,6 +831,14 @@ static int vgs_parse_numeric_argument(
         break;
 
     case TOKEN_WORD:
+        // If the token starts with `#` it is parsed as a color. If not, it
+        // must be a variable.
+
+        if (accept_colors && lexeme[0] == '#') {
+            ret = vgs_parse_color(log_ctx, arg, parser, &token);
+            break;
+        }
+
         ret = 1;
         for (int i = 0; i < VAR_COUNT; i++) {
             const char *var = parser->var_names[i];
@@ -825,9 +881,13 @@ static int vgs_parse_numeric_argument(
     return ret;
 }
 
-/// Check if the next token is a numeric value, so the last command must be
-/// repeated.
-static int vgs_parser_can_repeat_cmd(void *log_ctx, struct VGSParser *parser) {
+/// Check if the next token is a numeric value (or a color, if `accept_colors`
+/// is true), so the last command must be repeated.
+static int vgs_parser_can_repeat_cmd(
+    void *log_ctx,
+    struct VGSParser *parser,
+    bool accept_colors
+) {
     struct VGSParserToken token = { 0 };
 
     const int ret = vgs_parser_next_token(log_ctx, parser, &token, 0);
@@ -842,12 +902,17 @@ static int vgs_parser_can_repeat_cmd(void *log_ctx, 
struct VGSParser *parser) {
 
     case TOKEN_WORD:
         // If the next token is a word, it will be considered to repeat
-        // the command only if it is a variable, and there is not
-        // known command with the same name.
+        // the command only if it is a variable, and there is no known
+        // command with the same name.
+        //
+        // Color expressions are also valid if `accept_colors` is true.
 
         if (vgs_get_command(token.lexeme, token.length) != NULL)
             return 1;
 
+        if (accept_colors && token.lexeme[0] == '#')
+            return 0;
+
         for (int i = 0; i < VAR_COUNT; i++) {
             const char *var = parser->var_names[i];
             if (var == NULL)
@@ -925,7 +990,7 @@ static int vgs_parse_statement(
             // to append it to the current statement.
 
             if (statement.args_count < MAX_COMMAND_PARAMS
-                && vgs_parser_can_repeat_cmd(log_ctx, parser) == 0
+                && vgs_parser_can_repeat_cmd(log_ctx, parser, false) == 0
             ) {
                 param--;
             } else {
@@ -949,7 +1014,7 @@ static int vgs_parse_statement(
 
             // May repeat if the next token is numeric.
             if (param->type != PARAM_END
-                && vgs_parser_can_repeat_cmd(log_ctx, parser) == 0
+                && vgs_parser_can_repeat_cmd(log_ctx, parser, false) == 0
             ) {
                 param = &decl->params[0];
                 statement.args = NULL;
@@ -971,20 +1036,18 @@ static int vgs_parse_statement(
                     break;
 
                 if (vgs_token_is_string(&token, parser->var_names[i])) {
-                    arg.type = ARG_COLOR_VAR;
-                    arg.variable = i - VAR_U0;
+                    arg.type = ARG_VARIABLE;
+                    arg.variable = i;
                     break;
                 }
             }
 
-            if (arg.type == ARG_COLOR_VAR)
+            if (arg.type == ARG_VARIABLE)
                 break;
 
-            ret = av_parse_color(arg.color, token.lexeme, token.length, 
log_ctx);
-            if (ret != 0) {
-                vgs_log_invalid_token(log_ctx, parser, &token, "Expected 
color.");
+            ret = vgs_parse_color(log_ctx, &arg, parser, &token);
+            if (ret != 0)
                 FAIL(EINVAL);
-            }
 
             break;
 
@@ -1023,7 +1086,7 @@ static int vgs_parse_statement(
         }
 
         case PARAM_PROC_ARGS:
-            if (vgs_parser_can_repeat_cmd(log_ctx, parser) != 0) {
+            if (vgs_parser_can_repeat_cmd(log_ctx, parser, true) != 0) {
                 // No more arguments. Jump to next parameter.
                 param++;
                 continue;
@@ -1038,12 +1101,14 @@ static int vgs_parse_statement(
             /* fallthrough */
 
         case PARAM_NUMERIC:
+        case PARAM_NUMERIC_COLOR:
         case PARAM_NUMERIC_METADATA:
             ret = vgs_parse_numeric_argument(
                 log_ctx,
                 parser,
                 &arg,
-                param->type == PARAM_NUMERIC_METADATA
+                param->type == PARAM_NUMERIC_METADATA,
+                param->type == PARAM_NUMERIC_COLOR || param->type == 
PARAM_PROC_ARGS
             );
 
             if (ret != 0)
@@ -1333,9 +1398,6 @@ fail:
 /// Number of different states for the `randomg` function.
 #define RANDOM_STATES 4
 
-/// Cairo requires each color component to be a double.
-typedef double cairo_color[4];
-
 /// Block assigned to a procedure by a call to the `proc` command.
 struct VGSProcedure {
     const struct VGSProgram *program;
@@ -1561,8 +1623,7 @@ static int vgs_eval_state_init(
         state->vars[i] = NAN;
 
     for (int i = 0; i < FF_ARRAY_ELEMS(state->color_vars); i++)
-        for (int j = 0; j < FF_ARRAY_ELEMS(state->color_vars[i]); j++)
-            state->color_vars[i][j] = NAN;
+        color_reset(&state->color_vars[i]);
 
     return 0;
 }
@@ -1842,12 +1903,8 @@ static int vgs_eval(
 
             switch (a->type) {
             case ARG_COLOR:
-                for (int i = 0; i < FF_ARRAY_ELEMS(colors[arg]); i++)
-                    colors[arg][i] = ((double)a->color[i]) / 255.0;
-                break;
-
-            case ARG_COLOR_VAR:
-                memcpy(&colors[arg], &state->color_vars[a->variable], 
sizeof(cairo_color));
+                numerics[arg] = NAN;
+                color_copy(&colors[arg], a->color);
                 break;
 
             case ARG_EXPR:
@@ -1861,6 +1918,12 @@ static int vgs_eval(
             case ARG_VARIABLE:
                 av_assert0(a->variable < VAR_COUNT);
                 numerics[arg] = state->vars[a->variable];
+
+                if (a->variable >= VAR_U0)
+                    color_copy(&colors[arg], &state->color_vars[a->variable - 
VAR_U0]);
+                else
+                    color_reset(&colors[arg]);
+
                 break;
 
             default:
@@ -2165,26 +2228,38 @@ static int vgs_eval(
                 av_log(state->log_ctx, AV_LOG_ERROR,
                     "Missing body for procedure '%s'\n", proc_name);
             } else {
-                int ret;
                 double current_vars[MAX_PROC_ARGS] = { 0 };
+                cairo_color current_color_vars[MAX_PROC_ARGS];
 
                 // Set variables for the procedure arguments
                 for (int i = 0; i < proc_args; i++) {
                     const int var = proc->args[i];
-                    if (var != -1) {
-                        current_vars[i] = state->vars[var];
-                        state->vars[var] = numerics[i + 1];
-                    }
+                    if (var == -1)
+                        continue;
+
+                    const int color_var = var - VAR_U0;
+
+                    // Assign both color and numeric values.
+
+                    current_vars[i] = state->vars[var];
+                    color_copy(&current_color_vars[i], 
&state->color_vars[color_var]);
+
+                    state->vars[var] = numerics[i + 1];
+                    color_copy(&state->color_vars[color_var], &colors[i + 1]);
                 }
 
-                ret = vgs_eval(state, proc->program);
+                const int ret = vgs_eval(state, proc->program);
 
                 // Restore variable values.
                 for (int i = 0; i < proc_args; i++) {
                     const int var = proc->args[i];
-                    if (var != -1) {
-                        state->vars[var] = current_vars[i];
-                    }
+                    if (var == -1)
+                        continue;
+
+                    const int color_var = var - VAR_U0;
+
+                    color_copy(&state->color_vars[color_var], 
&current_color_vars[i]);
+                    state->vars[var] = current_vars[i];
                 }
 
                 if (ret != 0)
@@ -2399,9 +2474,11 @@ static int vgs_eval(
             ASSERT_ARGS(2);
 
             const int user_var = statement->args[0].constant;
-
             av_assert0(user_var >= VAR_U0 && user_var < (VAR_U0 + 
USER_VAR_COUNT));
+
+            color_copy(&state->color_vars[user_var - VAR_U0], &colors[1]);
             state->vars[user_var] = numerics[1];
+
             break;
         }
 
diff --git a/tests/ref/fate/filter-drawvg-interpreter 
b/tests/ref/fate/filter-drawvg-interpreter
index 3fc33e9c07..d05c7428ee 100644
--- a/tests/ref/fate/filter-drawvg-interpreter
+++ b/tests/ref/fate/filter-drawvg-interpreter
@@ -76,6 +76,16 @@ cairo_fill
 cairo_set_source #a8d8f0e6
 cairo_set_fill_rule 0
 cairo_fill
+cairo_set_source #abcdef66
+cairo_stroke
+cairo_set_source #123456ff
+cairo_stroke
+cairo_set_source #abcdef66
+cairo_stroke
+av_log[32]: [104:45] a0 = 1.000000 | [104:48] a2 = 123.000000
+cairo_set_source #50a0f033
+cairo_stroke
+av_log[32]: [104:45] a0 = -1.000000 | [104:48] a2 = -123.000000
 cairo_rel_line_to 1.0 3.0
 cairo_rel_line_to nan 0.0
 
diff --git a/tests/ref/lavf/drawvg.all b/tests/ref/lavf/drawvg.all
index 9603c8ed8f..9b1fc9234b 100644
--- a/tests/ref/lavf/drawvg.all
+++ b/tests/ref/lavf/drawvg.all
@@ -93,6 +93,18 @@ defhsla c1 200 0.7 0.8 0.9
 setcolor c0 fill
 setcolor c1 fill
 
+// Colors as arguments for setvar/call.
+setvar color0 #123456
+setvar color1 #[email protected]
+setvar color2 color1
+setvar a 123
+
+setcolor color2 stroke
+setcolor color0 stroke
+proc f3 a0 a1 a2 { setcolor a1 stroke print a0 a2 }
+call f3 1 color1 a
+call f3 -1 #[email protected] (-a)
+
 // Frame metadata
 getmetadata md0 m.a
 getmetadata md1 m.b

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to