FFmpeg
vf_drawvg.c
Go to the documentation of this file.
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 /**
20  * @file
21  *
22  * drawvg filter, draw vector graphics with cairo.
23  *
24  * This file contains the parser and the interpreter for VGS, and the
25  * AVClass definitions for the drawvg filter.
26  */
27 
28 #include <cairo.h>
29 #include <stdbool.h>
30 
31 #include "libavutil/avassert.h"
32 #include "libavutil/avstring.h"
33 #include "libavutil/bswap.h"
34 #include "libavutil/eval.h"
35 #include "libavutil/internal.h"
36 #include "libavutil/macros.h"
37 #include "libavutil/mem.h"
38 #include "libavutil/opt.h"
39 #include "libavutil/pixdesc.h"
40 #include "libavutil/sfc64.h"
41 
42 #include "avfilter.h"
43 #include "filters.h"
44 #include "textutils.h"
45 #include "video.h"
46 
47 /*
48  * == AVExpr Integration ==
49  *
50  * Definitions to use variables and functions in the expressions from
51  * `av_expr_*` functions.
52  *
53  * For user-variables, created with commands like `setvar` or `defhsla`,
54  * the VGS parser updates a copy of the `vgs_default_vars` array. The
55  * first user-variable is stored in the slot for `VAR_U0`.
56  */
57 
58 enum {
59  VAR_N, ///< Frame number.
60  VAR_T, ///< Timestamp in seconds.
61  VAR_TS, ///< Time in seconds of the first frame.
62  VAR_W, ///< Frame width.
63  VAR_H, ///< Frame height.
64  VAR_DURATION, ///< Frame duration.
65  VAR_CX, ///< X coordinate for current point.
66  VAR_CY, ///< Y coordinate for current point.
67  VAR_I, ///< Loop counter, to use with `repeat {}`.
68  VAR_U0, ///< User variables.
69 };
70 
71 /// Number of user variables that can be created with `setvar`.
72 ///
73 /// It is possible to allow any number of variables, but this
74 /// approach simplifies the implementation, and 20 variables
75 /// is more than enough for the expected use of this filter.
76 #define USER_VAR_COUNT 20
77 
78 /// Total number of variables (default- and user-variables).
79 #define VAR_COUNT (VAR_U0 + USER_VAR_COUNT)
80 
81 static const char *const vgs_default_vars[] = {
82  "n",
83  "t",
84  "ts",
85  "w",
86  "h",
87  "duration",
88  "cx",
89  "cy",
90  "i",
91  NULL, // User variables. Name is assigned by commands like `setvar`.
92 };
93 
94 // Functions used in expressions.
95 
96 static const char *const vgs_func1_names[] = {
97  "pathlen",
98  "randomg",
99  NULL,
100 };
101 
102 static double vgs_fn_pathlen(void *, double);
103 static double vgs_fn_randomg(void *, double);
104 
105 static double (*const vgs_func1_impls[])(void *, double) = {
108  NULL,
109 };
110 
111 static const char *const vgs_func2_names[] = {
112  "p",
113  NULL,
114 };
115 
116 static double vgs_fn_p(void *, double, double);
117 
118 static double (*const vgs_func2_impls[])(void *, double, double) = {
119  vgs_fn_p,
120  NULL,
121 };
122 
123 /*
124  * == Command Declarations ==
125  *
126  * Each command is defined by an opcode (used later by the interpreter), a name,
127  * and a set of parameters.
128  *
129  * Inspired by SVG, some commands can be repeated when the next token after the
130  * last parameter is a numeric value (for example, `L 1 2 3 4` is equivalent to
131  * `L 1 2 L 3 4`). In these commands, the last parameter is `PARAM_MAY_REPEAT`.
132  */
133 
135  CMD_ARC = 1, ///< arc (cx cy radius angle1 angle2)
136  CMD_ARC_NEG, ///< arcn (cx cy radius angle1 angle2)
137  CMD_BREAK, ///< break
138  CMD_CIRCLE, ///< circle (cx cy radius)
139  CMD_CLIP, ///< clip
140  CMD_CLIP_EO, ///< eoclip
141  CMD_CLOSE_PATH, ///< Z, z, closepath
142  CMD_COLOR_STOP, ///< colorstop (offset color)
143  CMD_CURVE_TO, ///< C, curveto (x1 y1 x2 y2 x y)
144  CMD_DEF_HSLA, ///< defhsla (varname h s l a)
145  CMD_DEF_RGBA, ///< defrgba (varname r g b a)
146  CMD_CURVE_TO_REL, ///< c, rcurveto (dx1 dy1 dx2 dy2 dx dy)
147  CMD_ELLIPSE, ///< ellipse (cx cy rx ry)
148  CMD_FILL, ///< fill
149  CMD_FILL_EO, ///< eofill
150  CMD_GET_METADATA, ///< getmetadata varname key
151  CMD_HORZ, ///< H (x)
152  CMD_HORZ_REL, ///< h (dx)
153  CMD_IF, ///< if (condition) { subprogram }
154  CMD_LINEAR_GRAD, ///< lineargrad (x0 y0 x1 y1)
155  CMD_LINE_TO, ///< L, lineto (x y)
156  CMD_LINE_TO_REL, ///< l, rlineto (dx dy)
157  CMD_MOVE_TO, ///< M, moveto (x y)
158  CMD_MOVE_TO_REL, ///< m, rmoveto (dx dy)
159  CMD_NEW_PATH, ///< newpath
160  CMD_PRESERVE, ///< preserve
161  CMD_PRINT, ///< print (expr)*
162  CMD_PROC_ASSIGN, ///< proc name varnames* { subprogram }
163  CMD_PROC_CALL, ///< call name (expr)*
164  CMD_Q_CURVE_TO, ///< Q (x1 y1 x y)
165  CMD_Q_CURVE_TO_REL, ///< q (dx1 dy1 dx dy)
166  CMD_RADIAL_GRAD, ///< radialgrad (cx0 cy0 radius0 cx1 cy1 radius1)
167  CMD_RECT, ///< rect (x y width height)
168  CMD_REPEAT, ///< repeat (count) { subprogram }
169  CMD_RESET_CLIP, ///< resetclip
170  CMD_RESET_DASH, ///< resetdash
171  CMD_RESET_MATRIX, ///< resetmatrix
172  CMD_RESTORE, ///< restore
173  CMD_ROTATE, ///< rotate (angle)
174  CMD_ROUNDEDRECT, ///< roundedrect (x y width height radius)
175  CMD_SAVE, ///< save
176  CMD_SCALE, ///< scale (s)
177  CMD_SCALEXY, ///< scalexy (sx sy)
178  CMD_SET_COLOR, ///< setcolor (color)
179  CMD_SET_DASH, ///< setdash (length)
180  CMD_SET_DASH_OFFSET, ///< setdashoffset (offset)
181  CMD_SET_HSLA, ///< sethsla (h s l a)
182  CMD_SET_LINE_CAP, ///< setlinecap (cap)
183  CMD_SET_LINE_JOIN, ///< setlinejoin (join)
184  CMD_SET_LINE_WIDTH, ///< setlinewidth (width)
185  CMD_SET_RGBA, ///< setrgba (r g b a)
186  CMD_SET_VAR, ///< setvar (varname value)
187  CMD_STROKE, ///< stroke
188  CMD_S_CURVE_TO, ///< S (x2 y2 x y)
189  CMD_S_CURVE_TO_REL, ///< s (dx2 dy2 dx dy)
190  CMD_TRANSLATE, ///< translate (tx ty)
191  CMD_T_CURVE_TO, ///< T (x y)
192  CMD_T_CURVE_TO_REL, ///< t (dx dy)
193  CMD_VERT, ///< V (y)
194  CMD_VERT_REL, ///< v (dy)
195 };
196 
197 /// Constants for some commands, like `setlinejoin`.
198 struct VGSConstant {
199  const char* name;
200  int value;
201 };
202 
203 static const struct VGSConstant vgs_consts_line_cap[] = {
204  { "butt", CAIRO_LINE_CAP_BUTT },
205  { "round", CAIRO_LINE_CAP_ROUND },
206  { "square", CAIRO_LINE_CAP_SQUARE },
207  { NULL, 0 },
208 };
209 
210 static const struct VGSConstant vgs_consts_line_join[] = {
211  { "bevel", CAIRO_LINE_JOIN_BEVEL },
212  { "miter", CAIRO_LINE_JOIN_MITER },
213  { "round", CAIRO_LINE_JOIN_ROUND },
214  { NULL, 0 },
215 };
216 
217 struct VGSParameter {
218  enum {
233  } type;
234 
235  const struct VGSConstant *constants; ///< Array for PARAM_CONSTANT.
236 };
237 
238 // Max number of parameters for a command.
239 #define MAX_COMMAND_PARAMS 8
240 
241 // Max number of arguments when calling a procedure. Subtract 2 to
242 // `MAX_COMMAND_PARAMS` because the call to `proc` needs 2 arguments
243 // (the procedure name and its body). The rest can be variable names
244 // for the arguments.
245 #define MAX_PROC_ARGS (MAX_COMMAND_PARAMS - 2)
246 
247 // Definition of each command.
248 
250  const char* name;
252  const struct VGSParameter *params;
253 };
254 
255 // Parameter lists.
256 #define PARAMS(...) (const struct VGSParameter[]){ __VA_ARGS__ }
257 #define L(...) PARAMS(__VA_ARGS__, { PARAM_END })
258 #define R(...) PARAMS(__VA_ARGS__, { PARAM_MAY_REPEAT })
259 #define NONE PARAMS({ PARAM_END })
260 
261 // Common parameter types.
262 #define N { PARAM_NUMERIC }
263 #define V { PARAM_VAR_NAME }
264 #define P { PARAM_SUBPROGRAM }
265 #define C(c) { PARAM_CONSTANT, .constants = c }
266 
267 // Declarations table.
268 //
269 // The array must be sorted by `name` in ascending order.
270 static const struct VGSCommandSpec vgs_commands[] = {
271  { "C", CMD_CURVE_TO, R(N, N, N, N, N, N) },
272  { "H", CMD_HORZ, R(N) },
273  { "L", CMD_LINE_TO, R(N, N) },
274  { "M", CMD_MOVE_TO, R(N, N) },
275  { "Q", CMD_Q_CURVE_TO, R(N, N, N, N) },
276  { "S", CMD_S_CURVE_TO, R(N, N, N, N) },
277  { "T", CMD_T_CURVE_TO, R(N, N) },
278  { "V", CMD_VERT, R(N) },
279  { "Z", CMD_CLOSE_PATH, NONE },
280  { "arc", CMD_ARC, R(N, N, N, N, N) },
281  { "arcn", CMD_ARC_NEG, R(N, N, N, N, N) },
282  { "break", CMD_BREAK, NONE },
283  { "c", CMD_CURVE_TO_REL, R(N, N, N, N, N, N) },
284  { "call", CMD_PROC_CALL, L({ PARAM_PROC_NAME }, { PARAM_PROC_ARGS }) },
285  { "circle", CMD_CIRCLE, R(N, N, N) },
286  { "clip", CMD_CLIP, NONE },
287  { "closepath", CMD_CLOSE_PATH, NONE },
288  { "colorstop", CMD_COLOR_STOP, R(N, { PARAM_COLOR }) },
289  { "curveto", CMD_CURVE_TO, R(N, N, N, N, N, N) },
290  { "defhsla", CMD_DEF_HSLA, L(V, N, N, N, N) },
291  { "defrgba", CMD_DEF_RGBA, L(V, N, N, N, N) },
292  { "ellipse", CMD_ELLIPSE, R(N, N, N, N) },
293  { "eoclip", CMD_CLIP_EO, NONE },
294  { "eofill", CMD_FILL_EO, NONE },
295  { "fill", CMD_FILL, NONE },
296  { "getmetadata", CMD_GET_METADATA, L(V, { PARAM_RAW_IDENT }) },
297  { "h", CMD_HORZ_REL, R(N) },
298  { "if", CMD_IF, L(N, P) },
299  { "l", CMD_LINE_TO_REL, R(N, N) },
300  { "lineargrad", CMD_LINEAR_GRAD, L(N, N, N, N) },
301  { "lineto", CMD_LINE_TO, R(N, N) },
302  { "m", CMD_MOVE_TO_REL, R(N, N) },
303  { "moveto", CMD_MOVE_TO, R(N, N) },
304  { "newpath", CMD_NEW_PATH, NONE },
305  { "preserve", CMD_PRESERVE, NONE },
306  { "print", CMD_PRINT, L({ PARAM_NUMERIC_METADATA }, { PARAM_VARIADIC }) },
307  { "proc", CMD_PROC_ASSIGN, L({ PARAM_PROC_NAME }, { PARAM_PROC_PARAMS }, P) },
308  { "q", CMD_Q_CURVE_TO_REL, R(N, N, N, N) },
309  { "radialgrad", CMD_RADIAL_GRAD, L(N, N, N, N, N, N) },
310  { "rcurveto", CMD_CURVE_TO_REL, R(N, N, N, N, N, N) },
311  { "rect", CMD_RECT, R(N, N, N, N) },
312  { "repeat", CMD_REPEAT, L(N, P) },
313  { "resetclip", CMD_RESET_CLIP, NONE },
314  { "resetdash", CMD_RESET_DASH, NONE },
315  { "resetmatrix", CMD_RESET_MATRIX, NONE },
316  { "restore", CMD_RESTORE, NONE },
317  { "rlineto", CMD_LINE_TO_REL, R(N, N) },
318  { "rmoveto", CMD_MOVE_TO_REL, R(N, N) },
319  { "rotate", CMD_ROTATE, L(N) },
320  { "roundedrect", CMD_ROUNDEDRECT, R(N, N, N, N, N) },
321  { "s", CMD_S_CURVE_TO_REL, R(N, N, N, N) },
322  { "save", CMD_SAVE, NONE },
323  { "scale", CMD_SCALE, L(N) },
324  { "scalexy", CMD_SCALEXY, L(N, N) },
325  { "setcolor", CMD_SET_COLOR, L({ PARAM_COLOR }) },
326  { "setdash", CMD_SET_DASH, R(N) },
327  { "setdashoffset", CMD_SET_DASH_OFFSET, R(N) },
328  { "sethsla", CMD_SET_HSLA, L(N, N, N, N) },
329  { "setlinecap", CMD_SET_LINE_CAP, L(C(vgs_consts_line_cap)) },
330  { "setlinejoin", CMD_SET_LINE_JOIN, L(C(vgs_consts_line_join)) },
331  { "setlinewidth", CMD_SET_LINE_WIDTH, L(N) },
332  { "setrgba", CMD_SET_RGBA, L(N, N, N, N) },
333  { "setvar", CMD_SET_VAR, L(V, { PARAM_NUMERIC_COLOR }) },
334  { "stroke", CMD_STROKE, NONE },
335  { "t", CMD_T_CURVE_TO_REL, R(N, N) },
336  { "translate", CMD_TRANSLATE, L(N, N) },
337  { "v", CMD_VERT_REL, R(N) },
338  { "z", CMD_CLOSE_PATH, NONE },
339 };
340 
341 #undef C
342 #undef L
343 #undef N
344 #undef NONE
345 #undef PARAMS
346 #undef R
347 
348 /// Comparator for `VGSCommandDecl`, to be used with `bsearch(3)`.
349 static int vgs_comp_command_spec(const void *cs1, const void *cs2) {
350  return strcmp(
351  ((const struct VGSCommandSpec*)cs1)->name,
352  ((const struct VGSCommandSpec*)cs2)->name
353  );
354 }
355 
356 /// Return the specs for the given command, or `NULL` if the name is not valid.
357 ///
358 /// The implementation assumes that `vgs_commands` is sorted by `name`.
359 static const struct VGSCommandSpec* vgs_get_command(const char *name, size_t length) {
360  char bufname[64];
361  struct VGSCommandSpec key = { .name = bufname };
362 
363  if (length >= sizeof(bufname))
364  return NULL;
365 
366  memcpy(bufname, name, length);
367  bufname[length] = '\0';
368 
369  return bsearch(
370  &key,
371  vgs_commands,
373  sizeof(vgs_commands[0]),
375  );
376 }
377 
378 /// Return `1` if the command changes the current path in the cairo context.
380  switch (cmd) {
381  case CMD_BREAK:
382  case CMD_COLOR_STOP:
383  case CMD_DEF_HSLA:
384  case CMD_DEF_RGBA:
385  case CMD_GET_METADATA:
386  case CMD_IF:
387  case CMD_LINEAR_GRAD:
388  case CMD_PRINT:
389  case CMD_PROC_ASSIGN:
390  case CMD_PROC_CALL:
391  case CMD_RADIAL_GRAD:
392  case CMD_REPEAT:
393  case CMD_RESET_DASH:
394  case CMD_RESET_MATRIX:
395  case CMD_SET_COLOR:
396  case CMD_SET_DASH:
397  case CMD_SET_DASH_OFFSET:
398  case CMD_SET_HSLA:
399  case CMD_SET_LINE_CAP:
400  case CMD_SET_LINE_JOIN:
401  case CMD_SET_LINE_WIDTH:
402  case CMD_SET_RGBA:
403  case CMD_SET_VAR:
404  return 0;
405 
406  default:
407  return 1;
408  }
409 }
410 
411 
412 /// Colors in cairo are defined by 4 values, between 0 and 1. Computed colors
413 /// (either by #RRGGBB expressions, or by commands like `defhsla`) are stored
414 /// in the values that will be sent to Cairo.
415 typedef double cairo_color[4];
416 
418  memcpy(dest, src, sizeof(cairo_color));
419 }
420 
421 static av_always_inline void color_reset(cairo_color *const dest) {
422  for (int i = 0; i < FF_ARRAY_ELEMS(*dest); i++)
423  (*dest)[i] = NAN;
424 }
425 
426 
427 /*
428  * == VGS Parser ==
429  *
430  * The lexer determines the token kind by reading the first character after a
431  * delimiter (any of " \n\t\r,").
432  *
433  * The output of the parser is an instance of `VGSProgram`. It is a list of
434  * statements, and each statement is a command opcode and its arguments. This
435  * instance is created on filter initialization, and reused for every frame.
436  *
437  * User-variables are stored in an array initialized with a copy of
438  * `vgs_default_vars`.
439  *
440  * Blocks (the body for procedures, `if`, and `repeat`) are stored as nested
441  * `VGSProgram` instances.
442  *
443  * The source is assumed to be ASCII. If it contains multibyte chars, each
444  * byte is treated as an individual character. This is only relevant when the
445  * parser must report the location of a syntax error.
446  *
447  * There is no error recovery. The first invalid token will stop the parser.
448  */
449 
450 struct VGSParser {
451  const char* source;
452  size_t cursor;
453 
454  const char **proc_names;
456 
457  // Store the variable names for the default ones (from `vgs_default_vars`)
458  // and the variables created with `setvar`.
459  //
460  // The extra slot is needed to store the `NULL` terminator expected by
461  // `av_expr_parse`.
462  const char *var_names[VAR_COUNT + 1];
463 };
464 
466  enum {
473  } type;
474 
475  const char *lexeme;
476  size_t position;
477  size_t length;
478 };
479 
480 /// Check if `token` is the value of `str`.
481 static int vgs_token_is_string(const struct VGSParserToken *token, const char *str) {
482  return strncmp(str, token->lexeme, token->length) == 0
483  && str[token->length] == '\0';
484 }
485 
486 /// Compute the line/column numbers of the given token.
487 static void vgs_token_span(
488  const struct VGSParser *parser,
489  const struct VGSParserToken *token,
490  size_t *line,
491  size_t *column
492 ) {
493  const char *source = parser->source;
494 
495  *line = 1;
496 
497  for (;;) {
498  const char *sep = strchr(source, '\n');
499 
500  if (sep == NULL || (sep - parser->source) > token->position) {
501  *column = token->position - (source - parser->source) + 1;
502  break;
503  }
504 
505  ++*line;
506  source = sep + 1;
507  }
508 }
509 
510 static av_printf_format(4, 5)
511 void vgs_log_invalid_token(
512  void *log_ctx,
513  const struct VGSParser *parser,
514  const struct VGSParserToken *token,
515  const char *extra_fmt,
516  ...
517 ) {
518  va_list ap;
519  char extra[256];
520  size_t line, column;
521 
522  vgs_token_span(parser, token, &line, &column);
523 
524  // Format extra message.
525  va_start(ap, extra_fmt);
526  vsnprintf(extra, sizeof(extra), extra_fmt, ap);
527  va_end(ap);
528 
529  av_log(log_ctx, AV_LOG_ERROR,
530  "Invalid token '%.*s' at line %zu, column %zu: %s\n",
531  (int)token->length, token->lexeme, line, column, extra);
532 }
533 
534 /// Return the next token in the source.
535 ///
536 /// @param[out] token Next token.
537 /// @param[in] advance If true, the cursor is updated after finding a token.
538 ///
539 /// @return `0` on success, and a negative `AVERROR` code on failure.
541  void *log_ctx,
542  struct VGSParser *parser,
543  struct VGSParserToken *token,
544  int advance
545 ) {
546 
547  #define WORD_SEPARATOR " \n\t\r,"
548 
549  int level;
550  size_t cursor, length;
551  const char *source;
552 
553 next_token:
554 
555  source = &parser->source[parser->cursor];
556 
557  cursor = strspn(source, WORD_SEPARATOR);
558  token->position = parser->cursor + cursor;
559  token->lexeme = &source[cursor];
560 
561  switch (source[cursor]) {
562  case '\0':
563  token->type = TOKEN_EOF;
564  token->lexeme = "<EOF>";
565  token->length = 5;
566  return 0;
567 
568  case '(':
569  // Find matching parenthesis.
570  level = 1;
571  length = 1;
572 
573  while (level > 0) {
574  switch (source[cursor + length]) {
575  case '\0':
576  token->length = 1; // Show only the '(' in the error message.
577  vgs_log_invalid_token(log_ctx, parser, token, "Unmatched parenthesis.");
578  return AVERROR(EINVAL);
579 
580  case '(':
581  level++;
582  break;
583 
584  case ')':
585  level--;
586  break;
587  }
588 
589  length++;
590  }
591 
592  token->type = TOKEN_EXPR;
593  token->length = length;
594  break;
595 
596  case '{':
597  token->type = TOKEN_LEFT_BRACKET;
598  token->length = 1;
599  break;
600 
601  case '}':
602  token->type = TOKEN_RIGHT_BRACKET;
603  token->length = 1;
604  break;
605 
606  case '+':
607  case '-':
608  case '.':
609  case '0':
610  case '1':
611  case '2':
612  case '3':
613  case '4':
614  case '5':
615  case '6':
616  case '7':
617  case '8':
618  case '9':
619  token->type = TOKEN_LITERAL;
620  token->length = strcspn(token->lexeme, WORD_SEPARATOR);
621  break;
622 
623  case '/':
624  // If the next character is also '/', ignore the rest of
625  // the line.
626  //
627  // If it is something else, return a `TOKEN_WORD`.
628  if (source[cursor + 1] == '/') {
629  parser->cursor += cursor + strcspn(token->lexeme, "\n");
630  goto next_token;
631  }
632 
633  /* fallthrough */
634 
635  default:
636  token->type = TOKEN_WORD;
637  token->length = strcspn(token->lexeme, WORD_SEPARATOR);
638  break;
639  }
640 
641  if (advance) {
642  parser->cursor += cursor + token->length;
643  }
644 
645  return 0;
646 }
647 
648 /// Command arguments.
649 struct VGSArgument {
650  enum {
659  } type;
660 
661  union {
663  int constant;
665  double literal;
666  int proc_id;
668  int variable;
669  };
670 
671  char *metadata;
672 };
673 
674 /// Program statements.
675 struct VGSStatement {
677  struct VGSArgument *args;
679 };
680 
681 struct VGSProgram {
684 
685  const char **proc_names;
687 };
688 
689 static void vgs_free(struct VGSProgram *program);
690 
691 static int vgs_parse(
692  void *log_ctx,
693  struct VGSParser *parser,
694  struct VGSProgram *program,
695  int subprogram
696 );
697 
698 static void vgs_statement_free(struct VGSStatement *stm) {
699  if (stm->args == NULL)
700  return;
701 
702  for (int j = 0; j < stm->args_count; j++) {
703  struct VGSArgument *arg = &stm->args[j];
704 
705  switch (arg->type) {
706  case ARG_COLOR:
707  av_freep(&arg->color);
708  break;
709 
710  case ARG_EXPR:
711  av_expr_free(arg->expr);
712  break;
713 
714  case ARG_SUBPROGRAM:
715  vgs_free(arg->subprogram);
716  av_freep(&arg->subprogram);
717  break;
718  }
719 
720  av_freep(&arg->metadata);
721  }
722 
723  av_freep(&stm->args);
724 }
725 
726 /// Release the memory allocated by the program.
727 static void vgs_free(struct VGSProgram *program) {
728  if (program->statements == NULL)
729  return;
730 
731  for (int i = 0; i < program->statements_count; i++)
732  vgs_statement_free(&program->statements[i]);
733 
734  av_freep(&program->statements);
735 
736  if (program->proc_names != NULL) {
737  for (int i = 0; i < program->proc_names_count; i++)
738  av_freep(&program->proc_names[i]);
739 
740  av_freep(&program->proc_names);
741  }
742 }
743 
744 static int vgs_parse_color(
745  void *log_ctx,
746  struct VGSArgument *arg,
747  const struct VGSParser *parser,
748  const struct VGSParserToken *token
749 ) {
750  uint8_t color[4];
751 
752  const int ret = av_parse_color(color, token->lexeme, token->length, log_ctx);
753  if (ret != 0) {
754  vgs_log_invalid_token(log_ctx, parser, token, "Expected color.");
755  return ret;
756  }
757 
758  arg->type = ARG_COLOR;
759  arg->color = av_malloc(sizeof(cairo_color));
760 
761  if (arg->color == NULL)
762  return AVERROR(ENOMEM);
763 
764  for (int i = 0; i < FF_ARRAY_ELEMS(*arg->color); i++)
765  (*arg->color)[i] = (double)color[i] / 255.0;
766 
767  return 0;
768 }
769 
770 /// Consume the next argument as a numeric value, and store it in `arg`.
771 ///
772 /// Return `0` on success, and a negative `AVERROR` code on failure.
774  void *log_ctx,
775  struct VGSParser *parser,
776  struct VGSArgument *arg,
777  int metadata,
778  bool accept_colors
779 ) {
780  int ret;
781  char stack_buf[64];
782  char *lexeme, *endp;
783  struct VGSParserToken token;
784 
785  ret = vgs_parser_next_token(log_ctx, parser, &token, 1);
786  if (ret != 0)
787  return ret;
788 
789  // Convert the lexeme to a NUL-terminated string. Small lexemes are copied
790  // to a buffer on the stack; thus, it avoids allocating memory is most cases.
791  if (token.length + 1 < sizeof(stack_buf)) {
792  lexeme = stack_buf;
793  } else {
794  lexeme = av_malloc(token.length + 1);
795 
796  if (lexeme == NULL)
797  return AVERROR(ENOMEM);
798  }
799 
800  memcpy(lexeme, token.lexeme, token.length);
801  lexeme[token.length] = '\0';
802 
803  switch (token.type) {
804  case TOKEN_LITERAL:
805  arg->type = ARG_LITERAL;
806  arg->literal = av_strtod(lexeme, &endp);
807 
808  if (*endp != '\0') {
809  vgs_log_invalid_token(log_ctx, parser, &token, "Expected valid number.");
810  ret = AVERROR(EINVAL);
811  }
812  break;
813 
814  case TOKEN_EXPR:
815  arg->type = ARG_EXPR;
816  ret = av_expr_parse(
817  &arg->expr,
818  lexeme,
819  parser->var_names,
824  0,
825  log_ctx
826  );
827 
828  if (ret != 0)
829  vgs_log_invalid_token(log_ctx, parser, &token, "Invalid expression.");
830 
831  break;
832 
833  case TOKEN_WORD:
834  // If the token starts with `#` it is parsed as a color. If not, it
835  // must be a variable.
836 
837  if (accept_colors && lexeme[0] == '#') {
838  ret = vgs_parse_color(log_ctx, arg, parser, &token);
839  break;
840  }
841 
842  ret = 1;
843  for (int i = 0; i < VAR_COUNT; i++) {
844  const char *var = parser->var_names[i];
845  if (var == NULL)
846  break;
847 
848  if (vgs_token_is_string(&token, var)) {
849  arg->type = ARG_VARIABLE;
850  arg->variable = i;
851  ret = 0;
852  break;
853  }
854  }
855 
856  if (ret == 0)
857  break;
858 
859  /* fallthrough */
860 
861  default:
862  vgs_log_invalid_token(log_ctx, parser, &token, "Expected numeric argument.");
863  ret = AVERROR(EINVAL);
864  }
865 
866  if (ret == 0) {
867  if (metadata) {
868  size_t line, column;
869  vgs_token_span(parser, &token, &line, &column);
870  arg->metadata = av_asprintf("[%zu:%zu] %s", line, column, lexeme);
871  } else {
872  arg->metadata = NULL;
873  }
874  } else {
875  memset(arg, 0, sizeof(*arg));
876  }
877 
878  if (lexeme != stack_buf)
879  av_freep(&lexeme);
880 
881  return ret;
882 }
883 
884 /// Check if the next token is a numeric value (or a color, if `accept_colors`
885 /// is true), so the last command must be repeated.
887  void *log_ctx,
888  struct VGSParser *parser,
889  bool accept_colors
890 ) {
891  struct VGSParserToken token = { 0 };
892 
893  const int ret = vgs_parser_next_token(log_ctx, parser, &token, 0);
894 
895  if (ret != 0)
896  return ret;
897 
898  switch (token.type) {
899  case TOKEN_EXPR:
900  case TOKEN_LITERAL:
901  return 0;
902 
903  case TOKEN_WORD:
904  // If the next token is a word, it will be considered to repeat
905  // the command only if it is a variable, and there is no known
906  // command with the same name.
907  //
908  // Color expressions are also valid if `accept_colors` is true.
909 
910  if (vgs_get_command(token.lexeme, token.length) != NULL)
911  return 1;
912 
913  if (accept_colors && token.lexeme[0] == '#')
914  return 0;
915 
916  for (int i = 0; i < VAR_COUNT; i++) {
917  const char *var = parser->var_names[i];
918  if (var == NULL)
919  return 1;
920 
921  if (vgs_token_is_string(&token, var))
922  return 0;
923  }
924 
925  return 1;
926 
927  default:
928  return 1;
929  }
930 }
931 
932 
933 static int vgs_is_valid_identifier(const struct VGSParserToken *token) {
934  // An identifier is valid if:
935  //
936  // - It starts with an alphabetic character or an underscore.
937  // - Everything else, alphanumeric or underscore
938 
939  for (int i = 0; i < token->length; i++) {
940  char c = token->lexeme[i];
941  if (c != '_'
942  && !(c >= 'a' && c <= 'z')
943  && !(c >= 'A' && c <= 'Z')
944  && !(i > 0 && c >= '0' && c <= '9')
945  ) {
946  return 0;
947  }
948  }
949 
950  return 1;
951 }
952 
953 /// Extract the arguments for a command, and add a new statement
954 /// to the program.
955 ///
956 /// On success, return `0`.
958  void *log_ctx,
959  struct VGSParser *parser,
960  struct VGSProgram *program,
961  const struct VGSCommandSpec *decl
962 ) {
963 
964  #define FAIL(err) \
965  do { \
966  vgs_statement_free(&statement); \
967  return AVERROR(err); \
968  } while(0)
969 
970  struct VGSStatement statement = {
971  .cmd = decl->cmd,
972  .args = NULL,
973  .args_count = 0,
974  };
975 
976  const struct VGSParameter *param = &decl->params[0];
977 
978  int proc_args_count = 0;
979 
980  for (;;) {
981  int ret;
982  void *r;
983 
984  struct VGSParserToken token = { 0 };
985  struct VGSArgument arg = { 0 };
986 
987  switch (param->type) {
988  case PARAM_VARIADIC:
989  // If the next token is numeric, repeat the previous parameter
990  // to append it to the current statement.
991 
992  if (statement.args_count < MAX_COMMAND_PARAMS
993  && vgs_parser_can_repeat_cmd(log_ctx, parser, false) == 0
994  ) {
995  param--;
996  } else {
997  param++;
998  }
999 
1000  continue;
1001 
1002  case PARAM_END:
1003  case PARAM_MAY_REPEAT:
1004  // Add the built statement to the program.
1005  r = av_dynarray2_add(
1006  (void*)&program->statements,
1007  &program->statements_count,
1008  sizeof(statement),
1009  (void*)&statement
1010  );
1011 
1012  if (r == NULL)
1013  FAIL(ENOMEM);
1014 
1015  // May repeat if the next token is numeric.
1016  if (param->type != PARAM_END
1017  && vgs_parser_can_repeat_cmd(log_ctx, parser, false) == 0
1018  ) {
1019  param = &decl->params[0];
1020  statement.args = NULL;
1021  statement.args_count = 0;
1022  continue;
1023  }
1024 
1025  return 0;
1026 
1027  case PARAM_COLOR:
1028  ret = vgs_parser_next_token(log_ctx, parser, &token, 1);
1029  if (ret != 0)
1030  FAIL(EINVAL);
1031 
1032  arg.type = ARG_COLOR;
1033 
1034  for (int i = VAR_U0; i < VAR_COUNT; i++) {
1035  if (parser->var_names[i] == NULL)
1036  break;
1037 
1038  if (vgs_token_is_string(&token, parser->var_names[i])) {
1039  arg.type = ARG_VARIABLE;
1040  arg.variable = i;
1041  break;
1042  }
1043  }
1044 
1045  if (arg.type == ARG_VARIABLE)
1046  break;
1047 
1048  ret = vgs_parse_color(log_ctx, &arg, parser, &token);
1049  if (ret != 0)
1050  FAIL(EINVAL);
1051 
1052  break;
1053 
1054  case PARAM_CONSTANT: {
1055  int found = 0;
1056  char expected_names[64] = { 0 };
1057 
1058  ret = vgs_parser_next_token(log_ctx, parser, &token, 1);
1059  if (ret != 0)
1060  FAIL(EINVAL);
1061 
1062  for (
1063  const struct VGSConstant *constant = param->constants;
1064  constant->name != NULL;
1065  constant++
1066  ) {
1067  if (vgs_token_is_string(&token, constant->name)) {
1068  arg.type = ARG_CONST;
1069  arg.constant = constant->value;
1070 
1071  found = 1;
1072  break;
1073  }
1074 
1075  // Collect valid names to include them in the error message, in case
1076  // the name is not found.
1077  av_strlcatf(expected_names, sizeof(expected_names), " '%s'", constant->name);
1078  }
1079 
1080  if (!found) {
1081  vgs_log_invalid_token(log_ctx, parser, &token, "Expected one of%s.", expected_names);
1082  FAIL(EINVAL);
1083  }
1084 
1085  break;
1086  }
1087 
1088  case PARAM_PROC_ARGS:
1089  if (vgs_parser_can_repeat_cmd(log_ctx, parser, true) != 0) {
1090  // No more arguments. Jump to next parameter.
1091  param++;
1092  continue;
1093  }
1094 
1095  if (proc_args_count++ >= MAX_PROC_ARGS) {
1096  vgs_log_invalid_token(log_ctx, parser, &token,
1097  "Too many arguments. Limit is %d", MAX_PROC_ARGS);
1098  FAIL(EINVAL);
1099  }
1100 
1101  /* fallthrough */
1102 
1103  case PARAM_NUMERIC:
1104  case PARAM_NUMERIC_COLOR:
1105  case PARAM_NUMERIC_METADATA:
1107  log_ctx,
1108  parser,
1109  &arg,
1110  param->type == PARAM_NUMERIC_METADATA,
1111  param->type == PARAM_NUMERIC_COLOR || param->type == PARAM_PROC_ARGS
1112  );
1113 
1114  if (ret != 0)
1115  FAIL(EINVAL);
1116 
1117  break;
1118 
1119  case PARAM_PROC_NAME: {
1120  int proc_id;
1121 
1122  ret = vgs_parser_next_token(log_ctx, parser, &token, 1);
1123  if (ret != 0)
1124  FAIL(EINVAL);
1125 
1126  if (!vgs_is_valid_identifier(&token)) {
1127  vgs_log_invalid_token(log_ctx, parser, &token, "Invalid procedure name.");
1128  FAIL(EINVAL);
1129  }
1130 
1131  // Use the index in the array as the identifier of the name.
1132 
1133  for (proc_id = 0; proc_id < parser->proc_names_count; proc_id++) {
1134  if (vgs_token_is_string(&token, parser->proc_names[proc_id]))
1135  break;
1136  }
1137 
1138  if (proc_id == parser->proc_names_count) {
1139  const char *name = av_strndup(token.lexeme, token.length);
1140 
1141  const char **r = av_dynarray2_add(
1142  (void*)&parser->proc_names,
1143  &parser->proc_names_count,
1144  sizeof(name),
1145  (void*)&name
1146  );
1147 
1148  if (r == NULL) {
1149  av_freep(&name);
1150  FAIL(ENOMEM);
1151  }
1152  }
1153 
1154  arg.type = ARG_PROCEDURE_ID;
1155  arg.proc_id = proc_id;
1156 
1157  break;
1158  }
1159 
1160  case PARAM_RAW_IDENT:
1161  ret = vgs_parser_next_token(log_ctx, parser, &token, 1);
1162  if (ret != 0)
1163  FAIL(EINVAL);
1164 
1165  switch (token.type) {
1166  case TOKEN_LITERAL:
1167  case TOKEN_WORD:
1168  arg.type = ARG_METADATA;
1169  arg.metadata = av_strndup(token.lexeme, token.length);
1170  break;
1171 
1172  default:
1173  vgs_log_invalid_token(log_ctx, parser, &token, "Expected '{'.");
1174  FAIL(EINVAL);
1175  }
1176 
1177  break;
1178 
1179  case PARAM_SUBPROGRAM:
1180  ret = vgs_parser_next_token(log_ctx, parser, &token, 1);
1181  if (ret != 0)
1182  FAIL(EINVAL);
1183 
1184  if (token.type != TOKEN_LEFT_BRACKET) {
1185  vgs_log_invalid_token(log_ctx, parser, &token, "Expected '{'.");
1186  FAIL(EINVAL);
1187  }
1188 
1189  arg.type = ARG_SUBPROGRAM;
1190  arg.subprogram = av_mallocz(sizeof(struct VGSProgram));
1191 
1192  ret = vgs_parse(log_ctx, parser, arg.subprogram, 1);
1193  if (ret != 0) {
1194  av_freep(&arg.subprogram);
1195  FAIL(EINVAL);
1196  }
1197 
1198  break;
1199 
1200  case PARAM_PROC_PARAMS:
1201  ret = vgs_parser_next_token(log_ctx, parser, &token, 0);
1202  if (ret != 0)
1203  FAIL(EINVAL);
1204 
1205  if (token.type == TOKEN_WORD && proc_args_count++ >= MAX_PROC_ARGS) {
1206  vgs_log_invalid_token(log_ctx, parser, &token,
1207  "Too many parameters. Limit is %d", MAX_PROC_ARGS);
1208  FAIL(EINVAL);
1209  }
1210 
1211  if (token.type != TOKEN_WORD) {
1212  // No more variables. Jump to next parameter.
1213  param++;
1214  continue;
1215  }
1216 
1217  /* fallthrough */
1218 
1219  case PARAM_VAR_NAME: {
1220  int var_idx = -1;
1221 
1222  ret = vgs_parser_next_token(log_ctx, parser, &token, 1);
1223  if (ret != 0)
1224  FAIL(EINVAL);
1225 
1226  // Find the slot where the variable is allocated, or the next
1227  // available slot if it is a new variable.
1228  for (int i = 0; i < VAR_COUNT; i++) {
1229  if (parser->var_names[i] == NULL
1230  || vgs_token_is_string(&token, parser->var_names[i])
1231  ) {
1232  var_idx = i;
1233  break;
1234  }
1235  }
1236 
1237  // No free slots to allocate new variables.
1238  if (var_idx == -1) {
1239  vgs_log_invalid_token(log_ctx, parser, &token,
1240  "Too many user variables. Can define up to %d variables.", USER_VAR_COUNT);
1241  FAIL(E2BIG);
1242  }
1243 
1244  // If the index is before `VAR_U0`, the name is already taken by
1245  // a default variable.
1246  if (var_idx < VAR_U0) {
1247  vgs_log_invalid_token(log_ctx, parser, &token, "Reserved variable name.");
1248  FAIL(EINVAL);
1249  }
1250 
1251  // Need to allocate a new variable.
1252  if (parser->var_names[var_idx] == NULL) {
1253  if (!vgs_is_valid_identifier(&token)) {
1254  vgs_log_invalid_token(log_ctx, parser, &token, "Invalid variable name.");
1255  FAIL(EINVAL);
1256  }
1257 
1258  parser->var_names[var_idx] = av_strndup(token.lexeme, token.length);
1259  }
1260 
1261  arg.type = ARG_CONST;
1262  arg.constant = var_idx;
1263  break;
1264  }
1265 
1266  default:
1267  av_assert0(0); /* unreachable */
1268  }
1269 
1270  r = av_dynarray2_add(
1271  (void*)&statement.args,
1272  &statement.args_count,
1273  sizeof(arg),
1274  (void*)&arg
1275  );
1276 
1277  if (r == NULL)
1278  FAIL(ENOMEM);
1279 
1280  switch (param->type) {
1281  case PARAM_PROC_ARGS:
1282  case PARAM_PROC_PARAMS:
1283  // Don't update params.
1284  break;
1285 
1286  default:
1287  param++;
1288  }
1289  }
1290 
1291  #undef FAIL
1292 }
1293 
1294 static void vgs_parser_init(struct VGSParser *parser, const char *source) {
1295  parser->source = source;
1296  parser->cursor = 0;
1297 
1298  parser->proc_names = NULL;
1299  parser->proc_names_count = 0;
1300 
1301  memset(parser->var_names, 0, sizeof(parser->var_names));
1302  for (int i = 0; i < VAR_U0; i++)
1303  parser->var_names[i] = vgs_default_vars[i];
1304 }
1305 
1306 static void vgs_parser_free(struct VGSParser *parser) {
1307  for (int i = VAR_U0; i < VAR_COUNT; i++)
1308  if (parser->var_names[i] != NULL)
1309  av_freep(&parser->var_names[i]);
1310 
1311  if (parser->proc_names != NULL) {
1312  for (int i = 0; i < parser->proc_names_count; i++)
1313  av_freep(&parser->proc_names[i]);
1314 
1315  av_freep(&parser->proc_names);
1316  }
1317 }
1318 
1319 /// Build a program by parsing a script.
1320 ///
1321 /// `subprogram` must be true when the function is called to parse the body of
1322 /// a block (like `if` or `proc` commands).
1323 ///
1324 /// Return `0` on success, and a negative `AVERROR` code on failure.
1325 static int vgs_parse(
1326  void *log_ctx,
1327  struct VGSParser *parser,
1328  struct VGSProgram *program,
1329  int subprogram
1330 ) {
1331  struct VGSParserToken token;
1332 
1333  memset(program, 0, sizeof(*program));
1334 
1335  for (;;) {
1336  int ret;
1337  const struct VGSCommandSpec *cmd;
1338 
1339  ret = vgs_parser_next_token(log_ctx, parser, &token, 1);
1340  if (ret != 0)
1341  goto fail;
1342 
1343  switch (token.type) {
1344  case TOKEN_EOF:
1345  if (subprogram) {
1346  vgs_log_invalid_token(log_ctx, parser, &token, "Expected '}'.");
1347  goto fail;
1348  } else {
1349  // Move the proc names to the main program.
1350  FFSWAP(const char **, program->proc_names, parser->proc_names);
1351  FFSWAP(int, program->proc_names_count, parser->proc_names_count);
1352  }
1353 
1354  return 0;
1355 
1356  case TOKEN_WORD:
1357  // The token must be a valid command.
1358  cmd = vgs_get_command(token.lexeme, token.length);
1359  if (cmd == NULL)
1360  goto invalid_token;
1361 
1362  ret = vgs_parse_statement(log_ctx, parser, program, cmd);
1363  if (ret != 0)
1364  goto fail;
1365 
1366  break;
1367 
1368  case TOKEN_RIGHT_BRACKET:
1369  if (!subprogram)
1370  goto invalid_token;
1371 
1372  return 0;
1373 
1374  default:
1375  goto invalid_token;
1376  }
1377  }
1378 
1379  return AVERROR_BUG; /* unreachable */
1380 
1381 invalid_token:
1382  vgs_log_invalid_token(log_ctx, parser, &token, "Expected command.");
1383 
1384 fail:
1385  vgs_free(program);
1386  return AVERROR(EINVAL);
1387 }
1388 
1389 /*
1390  * == Interpreter ==
1391  *
1392  * The interpreter takes the `VGSProgram` built by the parser, and translate the
1393  * statements to calls to cairo.
1394  *
1395  * `VGSEvalState` tracks the state needed to execute such commands.
1396  */
1397 
1398 /// Number of different states for the `randomg` function.
1399 #define RANDOM_STATES 4
1400 
1401 /// Block assigned to a procedure by a call to the `proc` command.
1403  const struct VGSProgram *program;
1404 
1405  /// Number of expected arguments.
1407 
1408  /// Variable ids where each argument is stored.
1410 };
1411 
1413  void *log_ctx;
1414 
1415  /// Current frame.
1417 
1418  /// Cairo context for drawing operations.
1419  cairo_t *cairo_ctx;
1420 
1421  /// Pattern being built by commands like `colorstop`.
1422  cairo_pattern_t *pattern_builder;
1423 
1424  /// Register if `break` was called in a subprogram.
1426 
1427  /// Next call to `[eo]fill`, `[eo]clip`, or `stroke`, should use
1428  /// the `_preserve` function.
1430 
1431  /// Subprograms associated to each procedure identifier.
1433 
1434  /// Reference to the procedure names in the `VGSProgram`.
1435  const char *const *proc_names;
1436 
1437  /// Values for the variables in expressions.
1438  ///
1439  /// Some variables (like `cx` or `cy`) are written before
1440  /// executing each statement.
1441  double vars[VAR_COUNT];
1442 
1443  /// Colors stored in variables.
1445 
1446  /// Track last color read by the `p()` function.
1447  struct {
1448  double numeric;
1449  uint8_t components[4];
1450  } last_fn_p_color;
1451 
1452  /// State for each index available for the `randomg` function.
1454 
1455  /// Frame metadata, if any.
1457 
1458  // Reflected Control Points. Used in T and S commands.
1459  //
1460  // See https://www.w3.org/TR/SVG/paths.html#ReflectedControlPoints
1461  struct {
1463 
1464  double cubic_x;
1465  double cubic_y;
1466  double quad_x;
1467  double quad_y;
1468  } rcp;
1469 };
1470 
1471 /// Function `pathlen(n)` for `av_expr_eval`.
1472 ///
1473 /// Compute the length of the current path in the cairo context. If `n > 0`, it
1474 /// is the maximum number of segments to be added to the length.
1475 static double vgs_fn_pathlen(void *data, double arg) {
1476  if (!isfinite(arg))
1477  return NAN;
1478 
1479  const struct VGSEvalState *state = (struct VGSEvalState *)data;
1480 
1481  int max_segments = (int)arg;
1482 
1483  double lmx = NAN, lmy = NAN; // last move point
1484  double cx = NAN, cy = NAN; // current point.
1485 
1486  double length = 0;
1487  cairo_path_t *path = cairo_copy_path_flat(state->cairo_ctx);
1488 
1489  for (int i = 0; i < path->num_data; i += path->data[i].header.length) {
1490  double x, y;
1491  cairo_path_data_t *data = &path->data[i];
1492 
1493  switch (data[0].header.type) {
1494  case CAIRO_PATH_MOVE_TO:
1495  cx = lmx = data[1].point.x;
1496  cy = lmy = data[1].point.y;
1497 
1498  // Don't update `length`.
1499  continue;
1500 
1501  case CAIRO_PATH_LINE_TO:
1502  x = data[1].point.x;
1503  y = data[1].point.y;
1504  break;
1505 
1506  case CAIRO_PATH_CLOSE_PATH:
1507  x = lmx;
1508  y = lmy;
1509  break;
1510 
1511  default:
1512  continue;
1513  }
1514 
1515  length += hypot(cx - x, cy - y);
1516 
1517  cx = x;
1518  cy = y;
1519 
1520  // If the function argument is `> 0`, use it as a limit for how
1521  // many segments are added up.
1522  if (--max_segments == 0)
1523  break;
1524  }
1525 
1526  cairo_path_destroy(path);
1527 
1528  return length;
1529 }
1530 
1531 /// Function `randomg(n)` for `av_expr_eval`.
1532 ///
1533 /// Compute a random value between 0 and 1. Similar to `random()`, but the
1534 /// state is global to the VGS program.
1535 ///
1536 /// The last 2 bits of the integer representation of the argument are used
1537 /// as the state index. If the state is not initialized, the argument is
1538 /// the seed for that state.
1539 static double vgs_fn_randomg(void *data, double arg) {
1540  if (!isfinite(arg))
1541  return arg;
1542 
1543  struct VGSEvalState *state = (struct VGSEvalState *)data;
1544 
1545  const uint64_t iarg = (uint64_t)arg;
1546  const int rng_idx = iarg % FF_ARRAY_ELEMS(state->random_state);
1547 
1548  FFSFC64 *rng = &state->random_state[rng_idx];
1549 
1550  if (rng->counter == 0)
1551  ff_sfc64_init(rng, iarg, iarg, iarg, 12);
1552 
1553  return ff_sfc64_get(rng) * (1.0 / UINT64_MAX);
1554 }
1555 
1556 /// Function `p(x, y)` for `av_expr_eval`.
1557 ///
1558 /// Return the pixel color in 0xRRGGBBAA format.
1559 ///
1560 /// The transformation matrix is applied to the given coordinates.
1561 ///
1562 /// If the coordinates are outside the frame, return NAN.
1563 static double vgs_fn_p(void* data, double x0, double y0) {
1564  struct VGSEvalState *state = (struct VGSEvalState *)data;
1565  const AVFrame *frame = state->frame;
1566 
1567  if (frame == NULL || !isfinite(x0) || !isfinite(y0))
1568  return NAN;
1569 
1570  cairo_user_to_device(state->cairo_ctx, &x0, &y0);
1571 
1572  const int x = (int)x0;
1573  const int y = (int)y0;
1574 
1575  if (x < 0 || y < 0 || x >= frame->width || y >= frame->height)
1576  return NAN;
1577 
1579 
1580  uint32_t color[4] = { 0, 0, 0, 255 };
1581 
1582  for (int c = 0; c < desc->nb_components; c++) {
1583  uint32_t pixel;
1585  &pixel,
1586  (void*)frame->data,
1587  frame->linesize,
1588  desc,
1589  x, y,
1590  c,
1591  1, // width
1592  0, // read_pal_component
1593  4 // dst_element_size
1594  );
1595 
1596  const int depth = desc->comp[c].depth;
1597  if (depth != 8)
1598  pixel = pixel * 255 / ((1 << depth) - 1);
1599 
1600  color[c] = pixel;
1601  }
1602 
1603  // A common use-case of `p()` is to store its result in a variable, so it
1604  // can be used later with commands like `setcolor` or `colorstop`:
1605  //
1606  // setvar pixel (p(0, 0))
1607  // ...
1608  // setcolor pixel
1609  //
1610  // Since we can't pass custom values through `av_expr_eval`, we store the
1611  // color in the `VGSEvalState` instance. Then, when a variable is assigned
1612  // in `setvar`, it checks if the value is the same as the output of `p()`.
1613  // In such case, it copies the color components to the slot in `color_vars`.
1614  //
1615  // This solution is far from perfect, but it works in all the documented
1616  // use-cases.
1617 
1618  const double num = color[0] << 24 | color[1] << 16 | color[2] << 8 | color[3];
1619 
1620  state->last_fn_p_color.numeric = num;
1621  for (int i = 0; i < FF_ARRAY_ELEMS(color); i++)
1622  state->last_fn_p_color.components[i] = (uint8_t)color[i];
1623 
1624  return num;
1625 }
1626 
1628  struct VGSEvalState *state,
1629  const struct VGSProgram *program,
1630  void *log_ctx,
1631  AVFrame *frame
1632 ) {
1633  memset(state, 0, sizeof(*state));
1634 
1635  state->log_ctx = log_ctx;
1636  state->frame = frame;
1637 
1638  state->rcp.status = RCP_NONE;
1639  state->last_fn_p_color.numeric = NAN;
1640 
1641  if (program->proc_names != NULL) {
1642  state->procedures = av_calloc(sizeof(struct VGSProcedure), program->proc_names_count);
1643  state->proc_names = program->proc_names;
1644 
1645  if (state->procedures == NULL)
1646  return AVERROR(ENOMEM);
1647  }
1648 
1649  for (int i = 0; i < FF_ARRAY_ELEMS(state->vars); i++)
1650  state->vars[i] = NAN;
1651 
1652  for (int i = 0; i < FF_ARRAY_ELEMS(state->color_vars); i++)
1653  color_reset(&state->color_vars[i]);
1654 
1655  return 0;
1656 }
1657 
1659  if (state->pattern_builder != NULL)
1660  cairo_pattern_destroy(state->pattern_builder);
1661 
1662  if (state->procedures != NULL)
1663  av_free(state->procedures);
1664 
1665  memset(state, 0, sizeof(*state));
1666 }
1667 
1668 /// Draw an ellipse. `x`/`y` specifies the center, and `rx`/`ry` the radius of
1669 /// the ellipse on the x/y axis.
1670 ///
1671 /// Cairo does not provide a native way to create an ellipse, but it can be done
1672 /// by scaling the Y axis with the transformation matrix.
1673 static void draw_ellipse(cairo_t *c, double x, double y, double rx, double ry) {
1674  cairo_save(c);
1675  cairo_translate(c, x, y);
1676 
1677  if (rx != ry)
1678  cairo_scale(c, 1, ry / rx);
1679 
1680  cairo_new_sub_path(c);
1681  cairo_arc(c, 0, 0, rx, 0, 2 * M_PI);
1682  cairo_close_path(c);
1683  cairo_new_sub_path(c);
1684 
1685  cairo_restore(c);
1686 }
1687 
1688 /// Draw a quadratic bezier from the current point to `x, y`, The control point
1689 /// is specified by `x1, y1`.
1690 ///
1691 /// If the control point is NAN, use the reflected point.
1692 ///
1693 /// cairo only supports cubic cuvers, so control points must be adjusted to
1694 /// simulate the behaviour in SVG.
1696  struct VGSEvalState *state,
1697  int relative,
1698  double x1,
1699  double y1,
1700  double x,
1701  double y
1702 ) {
1703  double x0 = 0, y0 = 0; // Current point.
1704  double xa, ya, xb, yb; // Control points for the cubic curve.
1705 
1706  const int use_reflected = isnan(x1);
1707 
1708  cairo_get_current_point(state->cairo_ctx, &x0, &y0);
1709 
1710  if (relative) {
1711  if (!use_reflected) {
1712  x1 += x0;
1713  y1 += y0;
1714  }
1715 
1716  x += x0;
1717  y += y0;
1718  }
1719 
1720  if (use_reflected) {
1721  if (state->rcp.status != RCP_NONE) {
1722  x1 = state->rcp.quad_x;
1723  y1 = state->rcp.quad_y;
1724  } else {
1725  x1 = x0;
1726  y1 = y0;
1727  }
1728  }
1729 
1730  xa = (x0 + 2 * x1) / 3;
1731  ya = (y0 + 2 * y1) / 3;
1732  xb = (x + 2 * x1) / 3;
1733  yb = (y + 2 * y1) / 3;
1734  cairo_curve_to(state->cairo_ctx, xa, ya, xb, yb, x, y);
1735 
1736  state->rcp.status = RCP_UPDATED;
1737  state->rcp.cubic_x = x1;
1738  state->rcp.cubic_y = y1;
1739  state->rcp.quad_x = 2 * x - x1;
1740  state->rcp.quad_y = 2 * y - y1;
1741 }
1742 
1743 /// Similar to quad_curve_to, but for cubic curves.
1745  struct VGSEvalState *state,
1746  int relative,
1747  double x1,
1748  double y1,
1749  double x2,
1750  double y2,
1751  double x,
1752  double y
1753 ) {
1754  double x0 = 0, y0 = 0; // Current point.
1755 
1756  const int use_reflected = isnan(x1);
1757 
1758  cairo_get_current_point(state->cairo_ctx, &x0, &y0);
1759 
1760  if (relative) {
1761  if (!use_reflected) {
1762  x1 += x0;
1763  y1 += y0;
1764  }
1765 
1766  x += x0;
1767  y += y0;
1768  x2 += x0;
1769  y2 += y0;
1770  }
1771 
1772  if (use_reflected) {
1773  if (state->rcp.status != RCP_NONE) {
1774  x1 = state->rcp.cubic_x;
1775  y1 = state->rcp.cubic_y;
1776  } else {
1777  x1 = x0;
1778  y1 = y0;
1779  }
1780  }
1781 
1782  cairo_curve_to(state->cairo_ctx, x1, y1, x2, y2, x, y);
1783 
1784  state->rcp.status = RCP_UPDATED;
1785  state->rcp.cubic_x = 2 * x - x2;
1786  state->rcp.cubic_y = 2 * y - y2;
1787  state->rcp.quad_x = x2;
1788  state->rcp.quad_y = y2;
1789 }
1790 
1791 static void draw_rounded_rect(
1792  cairo_t *c,
1793  double x,
1794  double y,
1795  double width,
1796  double height,
1797  double radius
1798 ) {
1799  radius = av_clipd(radius, 0, FFMIN(height / 2, width / 2));
1800 
1801  cairo_new_sub_path(c);
1802  cairo_arc(c, x + radius, y + radius, radius, M_PI, 3 * M_PI / 2);
1803  cairo_arc(c, x + width - radius, y + radius, radius, 3 * M_PI / 2, 2 * M_PI);
1804  cairo_arc(c, x + width - radius, y + height - radius, radius, 0, M_PI / 2);
1805  cairo_arc(c, x + radius, y + height - radius, radius, M_PI / 2, M_PI);
1806  cairo_close_path(c);
1807 }
1808 
1809 static void hsl2rgb(
1810  double h,
1811  double s,
1812  double l,
1813  double *pr,
1814  double *pg,
1815  double *pb
1816 ) {
1817  // https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB
1818 
1819  double r, g, b, chroma, x, h1;
1820 
1821  if (h < 0 || h >= 360)
1822  h = fmod(FFMAX(h, 0), 360);
1823 
1824  s = av_clipd(s, 0, 1);
1825  l = av_clipd(l, 0, 1);
1826 
1827  chroma = (1 - fabs(2 * l - 1)) * s;
1828  h1 = h / 60;
1829  x = chroma * (1 - fabs(fmod(h1, 2) - 1));
1830 
1831  switch ((int)floor(h1)) {
1832  case 0:
1833  r = chroma;
1834  g = x;
1835  b = 0;
1836  break;
1837 
1838  case 1:
1839  r = x;
1840  g = chroma;
1841  b = 0;
1842  break;
1843 
1844  case 2:
1845  r = 0;
1846  g = chroma;
1847  b = x;
1848  break;
1849 
1850  case 3:
1851  r = 0;
1852  g = x;
1853  b = chroma;
1854  break;
1855 
1856  case 4:
1857  r = x;
1858  g = 0;
1859  b = chroma;
1860  break;
1861 
1862  default:
1863  r = chroma;
1864  g = 0;
1865  b = x;
1866  break;
1867 
1868  }
1869 
1870  x = l - chroma / 2;
1871 
1872  *pr = r + x;
1873  *pg = g + x;
1874  *pb = b + x;
1875 }
1876 
1877 /// Interpreter for `VGSProgram`.
1878 ///
1879 /// Its implementation is a simple switch-based dispatch.
1880 ///
1881 /// To evaluate blocks (like `if` or `call`), it makes a recursive call with
1882 /// the subprogram allocated to the block.
1883 static int vgs_eval(
1884  struct VGSEvalState *state,
1885  const struct VGSProgram *program
1886 ) {
1887 
1888  #define ASSERT_ARGS(n) av_assert0(statement->args_count == n)
1889 
1890  // When `preserve` is used, the next call to `clip`, `fill`, or `stroke`
1891  // uses the `cairo_..._preserve` function.
1892  #define MAY_PRESERVE(funcname) \
1893  do { \
1894  if (state->preserve_path) { \
1895  state->preserve_path = 0; \
1896  funcname##_preserve(state->cairo_ctx); \
1897  } else { \
1898  funcname(state->cairo_ctx); \
1899  } \
1900  } while(0)
1901 
1902  double numerics[MAX_COMMAND_PARAMS];
1904 
1905  double cx, cy; // Current point.
1906 
1907  int relative;
1908 
1909  for (int st_number = 0; st_number < program->statements_count; st_number++) {
1910  const struct VGSStatement *statement = &program->statements[st_number];
1911 
1912  if (statement->args_count > FF_ARRAY_ELEMS(numerics)) {
1913  av_log(state->log_ctx, AV_LOG_ERROR, "Too many arguments (%d).\n", statement->args_count);
1914  return AVERROR_BUG;
1915  }
1916 
1917  if (cairo_has_current_point(state->cairo_ctx)) {
1918  cairo_get_current_point(state->cairo_ctx, &cx, &cy);
1919  } else {
1920  cx = NAN;
1921  cy = NAN;
1922  }
1923 
1924  state->vars[VAR_CX] = cx;
1925  state->vars[VAR_CY] = cy;
1926 
1927  // Compute arguments.
1928  for (int arg = 0; arg < statement->args_count; arg++) {
1929  const struct VGSArgument *a = &statement->args[arg];
1930 
1931  switch (a->type) {
1932  case ARG_COLOR:
1933  numerics[arg] = NAN;
1934  color_copy(&colors[arg], a->color);
1935  break;
1936 
1937  case ARG_EXPR:
1938  numerics[arg] = av_expr_eval(a->expr, state->vars, state);
1939  break;
1940 
1941  case ARG_LITERAL:
1942  numerics[arg] = a->literal;
1943  break;
1944 
1945  case ARG_VARIABLE:
1946  av_assert0(a->variable < VAR_COUNT);
1947  numerics[arg] = state->vars[a->variable];
1948 
1949  if (a->variable >= VAR_U0)
1950  color_copy(&colors[arg], &state->color_vars[a->variable - VAR_U0]);
1951  else
1952  color_reset(&colors[arg]);
1953 
1954  break;
1955 
1956  default:
1957  numerics[arg] = NAN;
1958  break;
1959  }
1960  }
1961 
1962  // If the command uses a pending pattern (like a solid color
1963  // or a gradient), set it to the cairo context before executing
1964  // stroke/fill commands.
1965  if (state->pattern_builder != NULL) {
1966  switch (statement->cmd) {
1967  case CMD_FILL:
1968  case CMD_FILL_EO:
1969  case CMD_RESTORE:
1970  case CMD_SAVE:
1971  case CMD_STROKE:
1972  cairo_set_source(state->cairo_ctx, state->pattern_builder);
1973  cairo_pattern_destroy(state->pattern_builder);
1974  state->pattern_builder = NULL;
1975  }
1976  }
1977 
1978  // Execute the command.
1979  switch (statement->cmd) {
1980  case CMD_ARC:
1981  ASSERT_ARGS(5);
1982  cairo_arc(
1983  state->cairo_ctx,
1984  numerics[0],
1985  numerics[1],
1986  numerics[2],
1987  numerics[3],
1988  numerics[4]
1989  );
1990  break;
1991 
1992  case CMD_ARC_NEG:
1993  ASSERT_ARGS(5);
1994  cairo_arc_negative(
1995  state->cairo_ctx,
1996  numerics[0],
1997  numerics[1],
1998  numerics[2],
1999  numerics[3],
2000  numerics[4]
2001  );
2002  break;
2003 
2004  case CMD_CIRCLE:
2005  ASSERT_ARGS(3);
2006  draw_ellipse(state->cairo_ctx, numerics[0], numerics[1], numerics[2], numerics[2]);
2007  break;
2008 
2009  case CMD_CLIP:
2010  case CMD_CLIP_EO:
2011  ASSERT_ARGS(0);
2012  cairo_set_fill_rule(
2013  state->cairo_ctx,
2014  statement->cmd == CMD_CLIP ?
2015  CAIRO_FILL_RULE_WINDING :
2016  CAIRO_FILL_RULE_EVEN_ODD
2017  );
2018 
2019  MAY_PRESERVE(cairo_clip);
2020  break;
2021 
2022  case CMD_CLOSE_PATH:
2023  ASSERT_ARGS(0);
2024  cairo_close_path(state->cairo_ctx);
2025  break;
2026 
2027  case CMD_COLOR_STOP:
2028  if (state->pattern_builder == NULL) {
2029  av_log(state->log_ctx, AV_LOG_ERROR, "colorstop with no active gradient.\n");
2030  break;
2031  }
2032 
2033  ASSERT_ARGS(2);
2034  cairo_pattern_add_color_stop_rgba(
2035  state->pattern_builder,
2036  numerics[0],
2037  colors[1][0],
2038  colors[1][1],
2039  colors[1][2],
2040  colors[1][3]
2041  );
2042  break;
2043 
2044  case CMD_CURVE_TO:
2045  case CMD_CURVE_TO_REL:
2046  ASSERT_ARGS(6);
2048  state,
2049  statement->cmd == CMD_CURVE_TO_REL,
2050  numerics[0],
2051  numerics[1],
2052  numerics[2],
2053  numerics[3],
2054  numerics[4],
2055  numerics[5]
2056  );
2057  break;
2058 
2059  case CMD_DEF_HSLA:
2060  case CMD_DEF_RGBA: {
2061  double r, g, b;
2062 
2063  ASSERT_ARGS(5);
2064 
2065  const int user_var = statement->args[0].variable;
2066  av_assert0(user_var >= VAR_U0 && user_var < (VAR_U0 + USER_VAR_COUNT));
2067 
2068  if (statement->cmd == CMD_DEF_HSLA) {
2069  hsl2rgb(numerics[1], numerics[2], numerics[3], &r, &g, &b);
2070  } else {
2071  r = numerics[1];
2072  g = numerics[2];
2073  b = numerics[3];
2074  }
2075 
2076  double *const color_var = state->color_vars[user_var - VAR_U0];
2077  color_var[0] = r;
2078  color_var[1] = g;
2079  color_var[2] = b;
2080  color_var[3] = numerics[4];
2081 
2082  break;
2083  }
2084 
2085  case CMD_ELLIPSE:
2086  ASSERT_ARGS(4);
2087  draw_ellipse(state->cairo_ctx, numerics[0], numerics[1], numerics[2], numerics[3]);
2088  break;
2089 
2090  case CMD_FILL:
2091  case CMD_FILL_EO:
2092  ASSERT_ARGS(0);
2093 
2094  cairo_set_fill_rule(
2095  state->cairo_ctx,
2096  statement->cmd == CMD_FILL ?
2097  CAIRO_FILL_RULE_WINDING :
2098  CAIRO_FILL_RULE_EVEN_ODD
2099  );
2100 
2101  MAY_PRESERVE(cairo_fill);
2102  break;
2103 
2104  case CMD_GET_METADATA: {
2105  ASSERT_ARGS(2);
2106 
2107  double value = NAN;
2108 
2109  const int user_var = statement->args[0].constant;
2110  const char *key = statement->args[1].metadata;
2111 
2112  av_assert0(user_var >= VAR_U0 && user_var < (VAR_U0 + USER_VAR_COUNT));
2113 
2114  if (state->metadata != NULL && key != NULL) {
2115  char *endp;
2116  AVDictionaryEntry *entry = av_dict_get(state->metadata, key, NULL, 0);
2117 
2118  if (entry != NULL) {
2119  value = av_strtod(entry->value, &endp);
2120 
2121  if (*endp != '\0')
2122  value = NAN;
2123  }
2124  }
2125 
2126  state->vars[user_var] = value;
2127  break;
2128  }
2129 
2130  case CMD_BREAK:
2131  state->interrupted = 1;
2132  return 0;
2133 
2134  case CMD_IF:
2135  ASSERT_ARGS(2);
2136 
2137  if (isfinite(numerics[0]) && numerics[0] != 0.0) {
2138  int ret = vgs_eval(state, statement->args[1].subprogram);
2139  if (ret != 0 || state->interrupted != 0)
2140  return ret;
2141  }
2142 
2143  break;
2144 
2145  case CMD_LINEAR_GRAD:
2146  ASSERT_ARGS(4);
2147 
2148  if (state->pattern_builder != NULL)
2149  cairo_pattern_destroy(state->pattern_builder);
2150 
2151  state->pattern_builder = cairo_pattern_create_linear(
2152  numerics[0],
2153  numerics[1],
2154  numerics[2],
2155  numerics[3]
2156  );
2157  break;
2158 
2159  case CMD_LINE_TO:
2160  ASSERT_ARGS(2);
2161  cairo_line_to(state->cairo_ctx, numerics[0], numerics[1]);
2162  break;
2163 
2164  case CMD_LINE_TO_REL:
2165  ASSERT_ARGS(2);
2166  cairo_rel_line_to(state->cairo_ctx, numerics[0], numerics[1]);
2167  break;
2168 
2169  case CMD_MOVE_TO:
2170  ASSERT_ARGS(2);
2171  cairo_move_to(state->cairo_ctx, numerics[0], numerics[1]);
2172  break;
2173 
2174  case CMD_MOVE_TO_REL:
2175  ASSERT_ARGS(2);
2176  cairo_rel_move_to(state->cairo_ctx, numerics[0], numerics[1]);
2177  break;
2178 
2179  case CMD_NEW_PATH:
2180  ASSERT_ARGS(0);
2181  cairo_new_sub_path(state->cairo_ctx);
2182  break;
2183 
2184  case CMD_PRESERVE:
2185  ASSERT_ARGS(0);
2186  state->preserve_path = 1;
2187  break;
2188 
2189  case CMD_PRINT: {
2190  char msg[256];
2191  int len = 0;
2192 
2193  for (int i = 0; i < statement->args_count; i++) {
2194  int written;
2195  int capacity = sizeof(msg) - len;
2196 
2197  written = snprintf(
2198  msg + len,
2199  capacity,
2200  "%s%s = %f",
2201  i > 0 ? " | " : "",
2202  statement->args[i].metadata,
2203  numerics[i]
2204  );
2205 
2206  // If buffer is too small, discard the latest arguments.
2207  if (written >= capacity)
2208  break;
2209 
2210  len += written;
2211  }
2212 
2213  av_log(state->log_ctx, AV_LOG_INFO, "%.*s\n", len, msg);
2214  break;
2215  }
2216 
2217  case CMD_PROC_ASSIGN: {
2218  struct VGSProcedure *proc;
2219 
2220  const int proc_args = statement->args_count - 2;
2221  av_assert0(proc_args >= 0 && proc_args <= MAX_PROC_ARGS);
2222 
2223  proc = &state->procedures[statement->args[0].proc_id];
2224  proc->program = statement->args[proc_args + 1].subprogram;
2225  proc->proc_args_count = proc_args;
2226 
2227  for (int i = 0; i < MAX_PROC_ARGS; i++)
2228  proc->args[i] = i < proc_args ? statement->args[i + 1].constant : -1;
2229 
2230  break;
2231  }
2232 
2233  case CMD_PROC_CALL: {
2234  const int proc_args = statement->args_count - 1;
2235  av_assert0(proc_args >= 0 && proc_args <= MAX_PROC_ARGS);
2236 
2237  const int proc_id = statement->args[0].proc_id;
2238 
2239  const struct VGSProcedure *proc = &state->procedures[proc_id];
2240 
2241  if (proc->proc_args_count != proc_args) {
2242  av_log(
2243  state->log_ctx,
2244  AV_LOG_ERROR,
2245  "Procedure expects %d arguments, but received %d.",
2246  proc->proc_args_count,
2247  proc_args
2248  );
2249 
2250  break;
2251  }
2252 
2253  if (proc->program == NULL) {
2254  const char *proc_name = state->proc_names[proc_id];
2255  av_log(state->log_ctx, AV_LOG_ERROR,
2256  "Missing body for procedure '%s'\n", proc_name);
2257  } else {
2258  double current_vars[MAX_PROC_ARGS] = { 0 };
2259  cairo_color current_color_vars[MAX_PROC_ARGS];
2260 
2261  // Set variables for the procedure arguments
2262  for (int i = 0; i < proc_args; i++) {
2263  const int var = proc->args[i];
2264  if (var == -1)
2265  continue;
2266 
2267  const int color_var = var - VAR_U0;
2268 
2269  // Assign both color and numeric values.
2270 
2271  current_vars[i] = state->vars[var];
2272  color_copy(&current_color_vars[i], &state->color_vars[color_var]);
2273 
2274  state->vars[var] = numerics[i + 1];
2275  color_copy(&state->color_vars[color_var], &colors[i + 1]);
2276  }
2277 
2278  const int ret = vgs_eval(state, proc->program);
2279 
2280  // Restore variable values.
2281  for (int i = 0; i < proc_args; i++) {
2282  const int var = proc->args[i];
2283  if (var == -1)
2284  continue;
2285 
2286  const int color_var = var - VAR_U0;
2287 
2288  color_copy(&state->color_vars[color_var], &current_color_vars[i]);
2289  state->vars[var] = current_vars[i];
2290  }
2291 
2292  if (ret != 0)
2293  return ret;
2294 
2295  // `break` interrupts the procedure, but don't stop the program.
2296  if (state->interrupted) {
2297  state->interrupted = 0;
2298  break;
2299  }
2300  }
2301 
2302  break;
2303  }
2304 
2305  case CMD_Q_CURVE_TO:
2306  case CMD_Q_CURVE_TO_REL:
2307  ASSERT_ARGS(4);
2308  relative = statement->cmd == CMD_Q_CURVE_TO_REL;
2310  state,
2311  relative,
2312  numerics[0],
2313  numerics[1],
2314  numerics[2],
2315  numerics[3]
2316  );
2317  break;
2318 
2319  case CMD_RADIAL_GRAD:
2320  ASSERT_ARGS(6);
2321 
2322  if (state->pattern_builder != NULL)
2323  cairo_pattern_destroy(state->pattern_builder);
2324 
2325  state->pattern_builder = cairo_pattern_create_radial(
2326  numerics[0],
2327  numerics[1],
2328  numerics[2],
2329  numerics[3],
2330  numerics[4],
2331  numerics[5]
2332  );
2333  break;
2334 
2335  case CMD_RESET_CLIP:
2336  cairo_reset_clip(state->cairo_ctx);
2337  break;
2338 
2339  case CMD_RESET_DASH:
2340  cairo_set_dash(state->cairo_ctx, NULL, 0, 0);
2341  break;
2342 
2343  case CMD_RESET_MATRIX:
2344  cairo_identity_matrix(state->cairo_ctx);
2345  break;
2346 
2347  case CMD_RECT:
2348  ASSERT_ARGS(4);
2349  cairo_rectangle(state->cairo_ctx, numerics[0], numerics[1], numerics[2], numerics[3]);
2350  break;
2351 
2352  case CMD_REPEAT: {
2353  double var_i = state->vars[VAR_I];
2354 
2355  ASSERT_ARGS(2);
2356 
2357  if (!isfinite(numerics[0]))
2358  break;
2359 
2360  for (int i = 0, count = (int)numerics[0]; i < count; i++) {
2361  state->vars[VAR_I] = i;
2362 
2363  const int ret = vgs_eval(state, statement->args[1].subprogram);
2364  if (ret != 0)
2365  return ret;
2366 
2367  // `break` interrupts the loop, but don't stop the program.
2368  if (state->interrupted) {
2369  state->interrupted = 0;
2370  break;
2371  }
2372  }
2373 
2374  state->vars[VAR_I] = var_i;
2375  break;
2376  }
2377 
2378  case CMD_RESTORE:
2379  ASSERT_ARGS(0);
2380  cairo_restore(state->cairo_ctx);
2381  break;
2382 
2383  case CMD_ROTATE:
2384  ASSERT_ARGS(1);
2385  cairo_rotate(state->cairo_ctx, numerics[0]);
2386  break;
2387 
2388  case CMD_ROUNDEDRECT:
2389  ASSERT_ARGS(5);
2391  state->cairo_ctx,
2392  numerics[0],
2393  numerics[1],
2394  numerics[2],
2395  numerics[3],
2396  numerics[4]
2397  );
2398  break;
2399 
2400  case CMD_SAVE:
2401  ASSERT_ARGS(0);
2402  cairo_save(state->cairo_ctx);
2403  break;
2404 
2405  case CMD_SCALE:
2406  ASSERT_ARGS(1);
2407  cairo_scale(state->cairo_ctx, numerics[0], numerics[0]);
2408  break;
2409 
2410  case CMD_SCALEXY:
2411  ASSERT_ARGS(2);
2412  cairo_scale(state->cairo_ctx, numerics[0], numerics[1]);
2413  break;
2414 
2415  case CMD_SET_COLOR:
2416  ASSERT_ARGS(1);
2417 
2418  if (state->pattern_builder != NULL)
2419  cairo_pattern_destroy(state->pattern_builder);
2420 
2421  state->pattern_builder = cairo_pattern_create_rgba(
2422  colors[0][0],
2423  colors[0][1],
2424  colors[0][2],
2425  colors[0][3]
2426  );
2427  break;
2428 
2429  case CMD_SET_LINE_CAP:
2430  ASSERT_ARGS(1);
2431  cairo_set_line_cap(state->cairo_ctx, statement->args[0].constant);
2432  break;
2433 
2434  case CMD_SET_LINE_JOIN:
2435  ASSERT_ARGS(1);
2436  cairo_set_line_join(state->cairo_ctx, statement->args[0].constant);
2437  break;
2438 
2439  case CMD_SET_LINE_WIDTH:
2440  ASSERT_ARGS(1);
2441  cairo_set_line_width(state->cairo_ctx, numerics[0]);
2442  break;
2443 
2444  case CMD_SET_DASH:
2445  case CMD_SET_DASH_OFFSET: {
2446  int num;
2447  double *dashes, offset, stack_buf[16];
2448 
2449  ASSERT_ARGS(1);
2450 
2451  num = cairo_get_dash_count(state->cairo_ctx);
2452 
2453  if (num + 1 < FF_ARRAY_ELEMS(stack_buf)) {
2454  dashes = stack_buf;
2455  } else {
2456  dashes = av_calloc(num + 1, sizeof(double));
2457 
2458  if (dashes == NULL)
2459  return AVERROR(ENOMEM);
2460  }
2461 
2462  cairo_get_dash(state->cairo_ctx, dashes, &offset);
2463 
2464  if (statement->cmd == CMD_SET_DASH) {
2465  dashes[num] = numerics[0];
2466  num++;
2467  } else {
2468  offset = numerics[0];
2469  }
2470 
2471  cairo_set_dash(state->cairo_ctx, dashes, num, offset);
2472 
2473  if (dashes != stack_buf)
2474  av_freep(&dashes);
2475 
2476  break;
2477  }
2478 
2479  case CMD_SET_HSLA:
2480  case CMD_SET_RGBA: {
2481  double r, g, b;
2482 
2483  ASSERT_ARGS(4);
2484 
2485  if (state->pattern_builder != NULL)
2486  cairo_pattern_destroy(state->pattern_builder);
2487 
2488  if (statement->cmd == CMD_SET_HSLA) {
2489  hsl2rgb(numerics[0], numerics[1], numerics[2], &r, &g, &b);
2490  } else {
2491  r = numerics[0];
2492  g = numerics[1];
2493  b = numerics[2];
2494  }
2495 
2496  state->pattern_builder = cairo_pattern_create_rgba(r, g, b, numerics[3]);
2497  break;
2498  }
2499 
2500  case CMD_SET_VAR: {
2501  ASSERT_ARGS(2);
2502 
2503  const int user_var = statement->args[0].constant;
2504  av_assert0(user_var >= VAR_U0 && user_var < (VAR_U0 + USER_VAR_COUNT));
2505 
2506  state->vars[user_var] = numerics[1];
2507 
2508  // Take the color from `p()`, if any.
2509  cairo_color *color = &state->color_vars[user_var - VAR_U0];
2510  if (state->last_fn_p_color.numeric == numerics[1]) {
2511  for (int i = 0; i < FF_ARRAY_ELEMS(*color); i++)
2512  (*color)[i] = state->last_fn_p_color.components[i] / 255.0;
2513  } else {
2514  color_copy(color, &colors[1]);
2515  }
2516 
2517  break;
2518  }
2519 
2520  case CMD_STROKE:
2521  ASSERT_ARGS(0);
2522  MAY_PRESERVE(cairo_stroke);
2523  break;
2524 
2525  case CMD_S_CURVE_TO:
2526  case CMD_S_CURVE_TO_REL:
2527  ASSERT_ARGS(4);
2529  state,
2530  statement->cmd == CMD_S_CURVE_TO_REL,
2531  NAN,
2532  NAN,
2533  numerics[0],
2534  numerics[1],
2535  numerics[2],
2536  numerics[3]
2537  );
2538  break;
2539 
2540  case CMD_TRANSLATE:
2541  ASSERT_ARGS(2);
2542  cairo_translate(state->cairo_ctx, numerics[0], numerics[1]);
2543  break;
2544 
2545  case CMD_T_CURVE_TO:
2546  case CMD_T_CURVE_TO_REL:
2547  ASSERT_ARGS(2);
2548  relative = statement->cmd == CMD_T_CURVE_TO_REL;
2549  draw_quad_curve_to(state, relative, NAN, NAN, numerics[0], numerics[1]);
2550  break;
2551 
2552  case CMD_HORZ:
2553  case CMD_HORZ_REL:
2554  case CMD_VERT:
2555  case CMD_VERT_REL:
2556  ASSERT_ARGS(1);
2557 
2558  if (cairo_has_current_point(state->cairo_ctx)) {
2559  double d = numerics[0];
2560 
2561  switch (statement->cmd) {
2562  case CMD_HORZ: cx = d; break;
2563  case CMD_VERT: cy = d; break;
2564  case CMD_HORZ_REL: cx += d; break;
2565  case CMD_VERT_REL: cy += d; break;
2566  }
2567 
2568  cairo_line_to(state->cairo_ctx, cx, cy);
2569  }
2570 
2571  break;
2572  }
2573 
2574  // Reflected control points will be discarded if the executed
2575  // command did not update them, and it is a commands to
2576  // modify the path.
2577  if (state->rcp.status == RCP_UPDATED) {
2578  state->rcp.status = RCP_VALID;
2579  } else if (vgs_cmd_change_path(statement->cmd)) {
2580  state->rcp.status = RCP_NONE;
2581  }
2582 
2583  // Check for errors in cairo.
2584  if (cairo_status(state->cairo_ctx) != CAIRO_STATUS_SUCCESS) {
2585  av_log(
2586  state->log_ctx,
2587  AV_LOG_ERROR,
2588  "Error in cairo context: %s\n",
2589  cairo_status_to_string(cairo_status(state->cairo_ctx))
2590  );
2591 
2592  return AVERROR(EINVAL);
2593  }
2594  }
2595 
2596  return 0;
2597 }
2598 
2599 /*
2600  * == AVClass for drawvg ==
2601  *
2602  * Source is parsed on the `init` function.
2603  *
2604  * Cairo supports a few pixel formats, but only RGB. All compatible formats are
2605  * listed in the `drawvg_pix_fmts` array.
2606  */
2607 
2608 typedef struct DrawVGContext {
2609  const AVClass *class;
2610 
2611  /// Equivalent to AVPixelFormat.
2612  cairo_format_t cairo_format;
2613 
2614  /// Time in seconds of the first frame.
2615  double time_start;
2616 
2617  /// Inline source.
2618  uint8_t *script_text;
2619 
2620  /// File path to load the source.
2621  uint8_t *script_file;
2622 
2624 } DrawVGContext;
2625 
2626 #define OPT(name, field, help) \
2627  { \
2628  name, \
2629  help, \
2630  offsetof(DrawVGContext, field), \
2631  AV_OPT_TYPE_STRING, \
2632  { .str = NULL }, \
2633  0, 0, \
2634  AV_OPT_FLAG_FILTERING_PARAM \
2635  | AV_OPT_FLAG_VIDEO_PARAM \
2636  }
2637 
2638 static const AVOption drawvg_options[]= {
2639  OPT("script", script_text, "script source to draw the graphics"),
2640  OPT("s", script_text, "script source to draw the graphics"),
2641  OPT("file", script_file, "file to load the script source"),
2642  { NULL }
2643 };
2644 
2645 #undef OPT
2646 
2647 
2648 AVFILTER_DEFINE_CLASS(drawvg);
2649 
2650 static const enum AVPixelFormat drawvg_pix_fmts[] = {
2656 };
2657 
2658 // Return the cairo equivalent to AVPixelFormat.
2659 static cairo_format_t cairo_format_from_pix_fmt(
2660  DrawVGContext* ctx,
2661  enum AVPixelFormat format
2662 ) {
2663  // This array must have the same order of `drawvg_pix_fmts`.
2664  const cairo_format_t format_map[] = {
2665  CAIRO_FORMAT_ARGB32, // cairo expects pre-multiplied alpha.
2666  CAIRO_FORMAT_RGB24,
2667  CAIRO_FORMAT_RGB16_565,
2668  CAIRO_FORMAT_RGB30,
2669  CAIRO_FORMAT_INVALID,
2670  };
2671 
2672  for (int i = 0; i < FF_ARRAY_ELEMS(drawvg_pix_fmts); i++) {
2673  if (drawvg_pix_fmts[i] == format)
2674  return format_map[i];
2675  }
2676 
2677  const char* name = av_get_pix_fmt_name(format);
2678  av_log(ctx, AV_LOG_ERROR, "Invalid pix_fmt: %s\n", name);
2679 
2680  return CAIRO_FORMAT_INVALID;
2681 }
2682 
2684  int ret;
2685  double var_t;
2686  cairo_surface_t* surface;
2687 
2689  AVFilterLink *outlink = inlink->dst->outputs[0];
2691  DrawVGContext *drawvg_ctx = filter_ctx->priv;
2692 
2693  struct VGSEvalState eval_state;
2694  ret = vgs_eval_state_init(&eval_state, &drawvg_ctx->program, drawvg_ctx, frame);
2695 
2696  if (ret != 0)
2697  return ret;
2698 
2699  // Draw directly on the frame data.
2700  surface = cairo_image_surface_create_for_data(
2701  frame->data[0],
2702  drawvg_ctx->cairo_format,
2703  frame->width,
2704  frame->height,
2705  frame->linesize[0]
2706  );
2707 
2708  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
2709  av_log(drawvg_ctx, AV_LOG_ERROR, "Failed to create cairo surface.\n");
2710  return AVERROR_EXTERNAL;
2711  }
2712 
2713  eval_state.cairo_ctx = cairo_create(surface);
2714 
2715  var_t = TS2T(frame->pts, inlink->time_base);
2716 
2717  if (isnan(drawvg_ctx->time_start))
2718  drawvg_ctx->time_start = var_t;
2719 
2720  eval_state.vars[VAR_N] = inl->frame_count_out;
2721  eval_state.vars[VAR_T] = var_t;
2722  eval_state.vars[VAR_TS] = drawvg_ctx->time_start;
2723  eval_state.vars[VAR_W] = inlink->w;
2724  eval_state.vars[VAR_H] = inlink->h;
2725  eval_state.vars[VAR_DURATION] = frame->duration * av_q2d(inlink->time_base);
2726 
2727  eval_state.metadata = frame->metadata;
2728 
2729  ret = vgs_eval(&eval_state, &drawvg_ctx->program);
2730 
2731  cairo_destroy(eval_state.cairo_ctx);
2732  cairo_surface_destroy(surface);
2733 
2734  vgs_eval_state_free(&eval_state);
2735 
2736  if (ret != 0)
2737  return ret;
2738 
2739  return ff_filter_frame(outlink, frame);
2740 }
2741 
2744  DrawVGContext *drawvg_ctx = filter_ctx->priv;
2745 
2746  // Find the cairo format equivalent to the format of the frame,
2747  // so cairo can draw directly on the memory already allocated.
2748 
2749  drawvg_ctx->cairo_format = cairo_format_from_pix_fmt(drawvg_ctx, inlink->format);
2750  if (drawvg_ctx->cairo_format == CAIRO_FORMAT_INVALID)
2751  return AVERROR(EINVAL);
2752 
2753  return 0;
2754 }
2755 
2757  int ret;
2758  struct VGSParser parser;
2759  DrawVGContext *drawvg = ctx->priv;
2760 
2761  drawvg->time_start = NAN;
2762 
2763  if ((drawvg->script_text == NULL) == (drawvg->script_file == NULL)) {
2765  "Either 'source' or 'file' must be provided\n");
2766 
2767  return AVERROR(EINVAL);
2768  }
2769 
2770  if (drawvg->script_file != NULL) {
2772  ctx,
2773  (const char *)drawvg->script_file,
2774  &drawvg->script_text,
2775  NULL
2776  );
2777 
2778  if (ret != 0)
2779  return ret;
2780  }
2781 
2782  vgs_parser_init(&parser, drawvg->script_text);
2783 
2784  ret = vgs_parse(drawvg, &parser, &drawvg->program, 0);
2785 
2786  vgs_parser_free(&parser);
2787 
2788  return ret;
2789 }
2790 
2792  DrawVGContext *drawvg = ctx->priv;
2793  vgs_free(&drawvg->program);
2794 }
2795 
2796 static const AVFilterPad drawvg_inputs[] = {
2797  {
2798  .name = "default",
2799  .type = AVMEDIA_TYPE_VIDEO,
2801  .filter_frame = drawvg_filter_frame,
2802  .config_props = drawvg_config_props,
2803  },
2804 };
2805 
2807  .p.name = "drawvg",
2808  .p.description = NULL_IF_CONFIG_SMALL("Draw vector graphics on top of video frames."),
2809  .p.priv_class = &drawvg_class,
2811  .priv_size = sizeof(DrawVGContext),
2812  .init = drawvg_init,
2813  .uninit = drawvg_uninit,
2817 };
VGSEvalState::random_state
FFSFC64 random_state[RANDOM_STATES]
State for each index available for the randomg function.
Definition: vf_drawvg.c:1453
drawvg_inputs
static const AVFilterPad drawvg_inputs[]
Definition: vf_drawvg.c:2796
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
name
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
CMD_VERT_REL
@ CMD_VERT_REL
v (dy)
Definition: vf_drawvg.c:194
CMD_PROC_ASSIGN
@ CMD_PROC_ASSIGN
proc name varnames* { subprogram }
Definition: vf_drawvg.c:162
entry
#define entry
Definition: aom_film_grain_template.c:66
level
uint8_t level
Definition: svq3.c:208
program
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C program
Definition: undefined.txt:6
VGSParameter::type
enum VGSParameter::@387 type
MAX_COMMAND_PARAMS
#define MAX_COMMAND_PARAMS
Definition: vf_drawvg.c:239
drawvg_options
static const AVOption drawvg_options[]
Definition: vf_drawvg.c:2638
VGSArgument::ARG_VARIABLE
@ ARG_VARIABLE
Definition: vf_drawvg.c:658
r
const char * r
Definition: vf_curves.c:127
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
CMD_COLOR_STOP
@ CMD_COLOR_STOP
colorstop (offset color)
Definition: vf_drawvg.c:142
VGSProcedure::args
int args[MAX_PROC_ARGS]
Variable ids where each argument is stored.
Definition: vf_drawvg.c:1409
cairo_set_dash
void cairo_set_dash(cairo_t *cr, const double *dashes, int num_dashes, double offset)
Definition: drawvg.c:155
VAR_T
@ VAR_T
Timestamp in seconds.
Definition: vf_drawvg.c:60
VGSParserToken::TOKEN_EXPR
@ TOKEN_EXPR
Definition: vf_drawvg.c:468
CMD_DEF_RGBA
@ CMD_DEF_RGBA
defrgba (varname r g b a)
Definition: vf_drawvg.c:145
AVFILTER_DEFINE_CLASS
AVFILTER_DEFINE_CLASS(drawvg)
FILTER_PIXFMTS_ARRAY
#define FILTER_PIXFMTS_ARRAY(array)
Definition: filters.h:243
VAR_CX
@ VAR_CX
X coordinate for current point.
Definition: vf_drawvg.c:65
color
Definition: vf_paletteuse.c:513
VGSParserToken::TOKEN_LEFT_BRACKET
@ TOKEN_LEFT_BRACKET
Definition: vf_drawvg.c:469
vgs_default_vars
static const char *const vgs_default_vars[]
Definition: vf_drawvg.c:81
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1067
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3456
av_parse_color
int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen, void *log_ctx)
Put the RGBA values that correspond to color_string in rgba_color.
Definition: parseutils.c:359
vgs_eval
static int vgs_eval(struct VGSEvalState *state, const struct VGSProgram *program)
Interpreter for VGSProgram.
Definition: vf_drawvg.c:1883
CMD_PRINT
@ CMD_PRINT
print (expr)*
Definition: vf_drawvg.c:161
CMD_Q_CURVE_TO
@ CMD_Q_CURVE_TO
Q (x1 y1 x y)
Definition: vf_drawvg.c:164
vgs_statement_free
static void vgs_statement_free(struct VGSStatement *stm)
Definition: vf_drawvg.c:698
vgs_fn_pathlen
static double vgs_fn_pathlen(void *, double)
Function pathlen(n) for av_expr_eval.
Definition: vf_drawvg.c:1475
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
av_asprintf
char * av_asprintf(const char *fmt,...)
Definition: avstring.c:115
N
#define N
Definition: vf_drawvg.c:262
CMD_ARC_NEG
@ CMD_ARC_NEG
arcn (cx cy radius angle1 angle2)
Definition: vf_drawvg.c:136
CMD_ROTATE
@ CMD_ROTATE
rotate (angle)
Definition: vf_drawvg.c:173
VGSEvalState::metadata
AVDictionary * metadata
Frame metadata, if any.
Definition: vf_drawvg.c:1456
FILTER_INPUTS
#define FILTER_INPUTS(array)
Definition: filters.h:263
CMD_SCALEXY
@ CMD_SCALEXY
scalexy (sx sy)
Definition: vf_drawvg.c:177
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:427
pixdesc.h
VGSParameter::PARAM_NUMERIC_METADATA
@ PARAM_NUMERIC_METADATA
Definition: vf_drawvg.c:225
CMD_RECT
@ CMD_RECT
rect (x y width height)
Definition: vf_drawvg.c:167
drawvg_init
static av_cold int drawvg_init(AVFilterContext *ctx)
Definition: vf_drawvg.c:2756
VGSArgument::type
enum VGSArgument::@389 type
vgs_comp_command_spec
static int vgs_comp_command_spec(const void *cs1, const void *cs2)
Comparator for VGSCommandDecl, to be used with bsearch(3).
Definition: vf_drawvg.c:349
av_dynarray2_add
void * av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size, const uint8_t *elem_data)
Add an element of size elem_size to a dynamic array.
Definition: mem.c:343
DrawVGContext::script_text
uint8_t * script_text
Inline source.
Definition: vf_drawvg.c:2618
AVOption
AVOption.
Definition: opt.h:429
b
#define b
Definition: input.c:42
chroma
static av_always_inline void chroma(WaveformContext *s, AVFrame *in, AVFrame *out, int component, int intensity, int offset_y, int offset_x, int column, int mirror, int jobnr, int nb_jobs)
Definition: vf_waveform.c:1639
data
const char data[16]
Definition: mxf.c:149
vgs_parse_numeric_argument
static int vgs_parse_numeric_argument(void *log_ctx, struct VGSParser *parser, struct VGSArgument *arg, int metadata, bool accept_colors)
Consume the next argument as a numeric value, and store it in arg.
Definition: vf_drawvg.c:773
CMD_T_CURVE_TO_REL
@ CMD_T_CURVE_TO_REL
t (dx dy)
Definition: vf_drawvg.c:192
VGSEvalState::status
enum VGSEvalState::@393::@394 status
drawvg_config_props
static int drawvg_config_props(AVFilterLink *inlink)
Definition: vf_drawvg.c:2742
color_reset
static av_always_inline void color_reset(cairo_color *const dest)
Definition: vf_drawvg.c:421
ff_sfc64_init
static void ff_sfc64_init(FFSFC64 *s, uint64_t seeda, uint64_t seedb, uint64_t seedc, int rounds)
Initialize sfc64 with up to 3 seeds.
Definition: sfc64.h:75
vgs_get_command
static const struct VGSCommandSpec * vgs_get_command(const char *name, size_t length)
Return the specs for the given command, or NULL if the name is not valid.
Definition: vf_drawvg.c:359
CMD_ARC
@ CMD_ARC
arc (cx cy radius angle1 angle2)
Definition: vf_drawvg.c:135
VGSArgument::literal
double literal
Definition: vf_drawvg.c:665
vgs_parser_next_token
static int vgs_parser_next_token(void *log_ctx, struct VGSParser *parser, struct VGSParserToken *token, int advance)
Return the next token in the source.
Definition: vf_drawvg.c:540
CMD_FILL_EO
@ CMD_FILL_EO
eofill
Definition: vf_drawvg.c:149
AVDictionary
Definition: dict.c:32
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
VGSStatement::args
struct VGSArgument * args
Definition: vf_drawvg.c:677
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:220
CMD_MOVE_TO
@ CMD_MOVE_TO
M, moveto (x y)
Definition: vf_drawvg.c:157
vgs_consts_line_join
static const struct VGSConstant vgs_consts_line_join[]
Definition: vf_drawvg.c:210
VGSEvalState::numeric
double numeric
Definition: vf_drawvg.c:1448
FAIL
#define FAIL(err)
NONE
#define NONE
Definition: vf_drawvg.c:259
VGSEvalState::preserve_path
int preserve_path
Next call to [eo]fill, [eo]clip, or stroke, should use the _preserve function.
Definition: vf_drawvg.c:1429
video.h
av_strlcatf
size_t av_strlcatf(char *dst, size_t size, const char *fmt,...)
Definition: avstring.c:103
CMD_SET_LINE_CAP
@ CMD_SET_LINE_CAP
setlinecap (cap)
Definition: vf_drawvg.c:182
OPT
#define OPT(name, field, help)
Definition: vf_drawvg.c:2626
VAR_U0
@ VAR_U0
User variables.
Definition: vf_drawvg.c:68
cairo_format_from_pix_fmt
static cairo_format_t cairo_format_from_pix_fmt(DrawVGContext *ctx, enum AVPixelFormat format)
Definition: vf_drawvg.c:2659
vgs_parser_free
static void vgs_parser_free(struct VGSParser *parser)
Definition: vf_drawvg.c:1306
CMD_RESET_CLIP
@ CMD_RESET_CLIP
resetclip
Definition: vf_drawvg.c:169
CMD_IF
@ CMD_IF
if (condition) { subprogram }
Definition: vf_drawvg.c:153
av_expr_parse
int av_expr_parse(AVExpr **expr, const char *s, const char *const *const_names, const char *const *func1_names, double(*const *funcs1)(void *, double), const char *const *func2_names, double(*const *funcs2)(void *, double, double), int log_offset, void *log_ctx)
Parse an expression.
Definition: eval.c:710
VGSArgument::subprogram
struct VGSProgram * subprogram
Definition: vf_drawvg.c:667
VGSEvalState::procedures
struct VGSProcedure * procedures
Subprograms associated to each procedure identifier.
Definition: vf_drawvg.c:1432
cairo_get_dash_count
cairo_bool_t cairo_get_dash_count(cairo_t *cr)
Definition: drawvg.c:138
CMD_SET_DASH
@ CMD_SET_DASH
setdash (length)
Definition: vf_drawvg.c:179
VGSArgument::ARG_CONST
@ ARG_CONST
Definition: vf_drawvg.c:652
VGSCommand
VGSCommand
Definition: vf_drawvg.c:134
VGSParserToken::TOKEN_EOF
@ TOKEN_EOF
Definition: vf_drawvg.c:467
CMD_SET_RGBA
@ CMD_SET_RGBA
setrgba (r g b a)
Definition: vf_drawvg.c:185
vgs_func2_impls
static double(*const vgs_func2_impls[])(void *, double, double)
Definition: vf_drawvg.c:118
VGSProgram::proc_names
const char ** proc_names
Definition: vf_drawvg.c:685
VGSConstant
Constants for some commands, like setlinejoin.
Definition: vf_drawvg.c:198
CMD_RESET_MATRIX
@ CMD_RESET_MATRIX
resetmatrix
Definition: vf_drawvg.c:171
macros.h
fail
#define fail()
Definition: checkasm.h:216
VGSProgram::statements_count
int statements_count
Definition: vf_drawvg.c:683
VGSProgram
Definition: vf_drawvg.c:681
FFSFC64
Definition: sfc64.h:37
drawvg_uninit
static av_cold void drawvg_uninit(AVFilterContext *ctx)
Definition: vf_drawvg.c:2791
VGSEvalState::cubic_y
double cubic_y
Definition: vf_drawvg.c:1465
vgs_fn_randomg
static double vgs_fn_randomg(void *, double)
Function randomg(n) for av_expr_eval.
Definition: vf_drawvg.c:1539
CMD_CLIP
@ CMD_CLIP
clip
Definition: vf_drawvg.c:139
filter_ctx
static FilteringContext * filter_ctx
Definition: transcode.c:52
VGSEvalState::RCP_NONE
@ RCP_NONE
Definition: vf_drawvg.c:1462
CMD_MOVE_TO_REL
@ CMD_MOVE_TO_REL
m, rmoveto (dx dy)
Definition: vf_drawvg.c:158
VGSParser::proc_names
const char ** proc_names
Definition: vf_drawvg.c:454
vgs_token_is_string
static int vgs_token_is_string(const struct VGSParserToken *token, const char *str)
Check if token is the value of str.
Definition: vf_drawvg.c:481
VGSArgument::ARG_LITERAL
@ ARG_LITERAL
Definition: vf_drawvg.c:654
av_expr_free
void av_expr_free(AVExpr *e)
Free a parsed expression previously created with av_expr_parse().
Definition: eval.c:358
VGSParserToken::length
size_t length
Definition: vf_drawvg.c:477
AVFilterPad
A filter pad used for either input or output.
Definition: filters.h:39
VGSArgument::proc_id
int proc_id
Definition: vf_drawvg.c:666
VAR_COUNT
#define VAR_COUNT
Total number of variables (default- and user-variables).
Definition: vf_drawvg.c:79
avassert.h
vgs_parser_can_repeat_cmd
static int vgs_parser_can_repeat_cmd(void *log_ctx, struct VGSParser *parser, bool accept_colors)
Check if the next token is a numeric value (or a color, if accept_colors is true),...
Definition: vf_drawvg.c:886
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
av_cold
#define av_cold
Definition: attributes.h:106
CMD_CIRCLE
@ CMD_CIRCLE
circle (cx cy radius)
Definition: vf_drawvg.c:138
ff_load_textfile
int ff_load_textfile(void *log_ctx, const char *textfile, unsigned char **text, size_t *text_size)
Definition: textutils.c:354
VAR_CY
@ VAR_CY
Y coordinate for current point.
Definition: vf_drawvg.c:66
FFSFC64::counter
uint64_t counter
Definition: sfc64.h:38
ff_video_default_filterpad
const AVFilterPad ff_video_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_VIDEO.
Definition: video.c:37
FFFilter
Definition: filters.h:266
WORD_SEPARATOR
#define WORD_SEPARATOR
ff_vf_drawvg
const FFFilter ff_vf_drawvg
Definition: vf_drawvg.c:2806
av_dict_get
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
Definition: dict.c:60
cairo_color
double cairo_color[4]
Colors in cairo are defined by 4 values, between 0 and 1.
Definition: vf_drawvg.c:415
CMD_Q_CURVE_TO_REL
@ CMD_Q_CURVE_TO_REL
q (dx1 dy1 dx dy)
Definition: vf_drawvg.c:165
s
#define s(width, name)
Definition: cbs_vp9.c:198
VGSArgument::constant
int constant
Definition: vf_drawvg.c:663
floor
static __device__ float floor(float a)
Definition: cuda_runtime.h:173
g
const char * g
Definition: vf_curves.c:128
VGSParserToken::type
enum VGSParserToken::@388 type
CMD_STROKE
@ CMD_STROKE
stroke
Definition: vf_drawvg.c:187
L
#define L(...)
Definition: vf_drawvg.c:257
av_q2d
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition: rational.h:104
VGSParameter::PARAM_VAR_NAME
@ PARAM_VAR_NAME
Definition: vf_drawvg.c:232
vgs_is_valid_identifier
static int vgs_is_valid_identifier(const struct VGSParserToken *token)
Definition: vf_drawvg.c:933
VGSArgument::variable
int variable
Definition: vf_drawvg.c:668
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:41
filters.h
VGSArgument
Command arguments.
Definition: vf_drawvg.c:649
ctx
AVFormatContext * ctx
Definition: movenc.c:49
VAR_N
@ VAR_N
Frame number.
Definition: vf_drawvg.c:59
av_expr_eval
double av_expr_eval(AVExpr *e, const double *const_values, void *opaque)
Evaluate a previously parsed expression.
Definition: eval.c:792
VGSProgram::proc_names_count
int proc_names_count
Definition: vf_drawvg.c:686
VGSParserToken::TOKEN_RIGHT_BRACKET
@ TOKEN_RIGHT_BRACKET
Definition: vf_drawvg.c:471
VGSParameter::PARAM_PROC_ARGS
@ PARAM_PROC_ARGS
Definition: vf_drawvg.c:226
VGSParser::cursor
size_t cursor
Definition: vf_drawvg.c:452
isfinite
#define isfinite(x)
Definition: libm.h:361
sfc64.h
AVExpr
Definition: eval.c:158
cairo_set_source
void cairo_set_source(cairo_t *cr, cairo_pattern_t *source)
Definition: drawvg.c:171
VGSArgument::ARG_PROCEDURE_ID
@ ARG_PROCEDURE_ID
Definition: vf_drawvg.c:656
key
const char * key
Definition: hwcontext_opencl.c:189
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
VGSParameter::PARAM_PROC_NAME
@ PARAM_PROC_NAME
Definition: vf_drawvg.c:227
FILTER_OUTPUTS
#define FILTER_OUTPUTS(array)
Definition: filters.h:264
av_read_image_line2
void av_read_image_line2(void *dst, const uint8_t *data[4], const int linesize[4], const AVPixFmtDescriptor *desc, int x, int y, int c, int w, int read_pal_component, int dst_element_size)
Read a line from an image, and write the values of the pixel format component c to dst.
Definition: pixdesc.c:31
NAN
#define NAN
Definition: mathematics.h:115
vgs_free
static void vgs_free(struct VGSProgram *program)
Release the memory allocated by the program.
Definition: vf_drawvg.c:727
arg
const char * arg
Definition: jacosubdec.c:65
cairo_has_current_point
cairo_bool_t cairo_has_current_point(cairo_t *cr)
Definition: drawvg.c:162
VGSParameter::PARAM_CONSTANT
@ PARAM_CONSTANT
Definition: vf_drawvg.c:220
CMD_LINE_TO_REL
@ CMD_LINE_TO_REL
l, rlineto (dx dy)
Definition: vf_drawvg.c:156
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
fabs
static __device__ float fabs(float a)
Definition: cuda_runtime.h:182
metadata
Stream codec metadata
Definition: ogg-flac-chained-meta.txt:2
NULL
#define NULL
Definition: coverity.c:32
format
New swscale design to change SwsGraph is what coordinates multiple passes These can include cascaded scaling error diffusion and so on Or we could have separate passes for the vertical and horizontal scaling In between each SwsPass lies a fully allocated image buffer Graph passes may have different levels of e g we can have a single threaded error diffusion pass following a multi threaded scaling pass SwsGraph is internally recreated whenever the image format
Definition: swscale-v2.txt:14
pixel
uint8_t pixel
Definition: tiny_ssim.c:41
cairo_get_dash
void cairo_get_dash(cairo_t *cr, double *dashes, double *offset)
Definition: drawvg.c:146
VGSParserToken
Definition: vf_drawvg.c:465
VAR_H
@ VAR_H
Frame height.
Definition: vf_drawvg.c:63
VGSArgument::ARG_EXPR
@ ARG_EXPR
Definition: vf_drawvg.c:653
isnan
#define isnan(x)
Definition: libm.h:342
CMD_BREAK
@ CMD_BREAK
break
Definition: vf_drawvg.c:137
VGSEvalState::vars
double vars[VAR_COUNT]
Values for the variables in expressions.
Definition: vf_drawvg.c:1441
textutils.h
CMD_CURVE_TO_REL
@ CMD_CURVE_TO_REL
c, rcurveto (dx1 dy1 dx2 dy2 dx dy)
Definition: vf_drawvg.c:146
vgs_func1_impls
static double(*const vgs_func1_impls[])(void *, double)
Definition: vf_drawvg.c:105
vgs_parse
static int vgs_parse(void *log_ctx, struct VGSParser *parser, struct VGSProgram *program, int subprogram)
Build a program by parsing a script.
Definition: vf_drawvg.c:1325
double
double
Definition: af_crystalizer.c:132
CMD_S_CURVE_TO
@ CMD_S_CURVE_TO
S (x2 y2 x y)
Definition: vf_drawvg.c:188
VGSEvalState::pattern_builder
cairo_pattern_t * pattern_builder
Pattern being built by commands like colorstop.
Definition: vf_drawvg.c:1422
VGSParser
Definition: vf_drawvg.c:450
VGSParserToken::TOKEN_WORD
@ TOKEN_WORD
Definition: vf_drawvg.c:472
VGSEvalState::last_fn_p_color
struct VGSEvalState::@392 last_fn_p_color
Track last color read by the p() function.
draw_ellipse
static void draw_ellipse(cairo_t *c, double x, double y, double rx, double ry)
Draw an ellipse.
Definition: vf_drawvg.c:1673
CMD_REPEAT
@ CMD_REPEAT
repeat (count) { subprogram }
Definition: vf_drawvg.c:168
VGSEvalState::quad_x
double quad_x
Definition: vf_drawvg.c:1466
color_copy
static av_always_inline void color_copy(cairo_color *dest, const cairo_color *src)
Definition: vf_drawvg.c:417
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
VAR_DURATION
@ VAR_DURATION
Frame duration.
Definition: vf_drawvg.c:64
ff_filter_link
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition: filters.h:198
DrawVGContext::script_file
uint8_t * script_file
File path to load the source.
Definition: vf_drawvg.c:2621
VGSParameter::PARAM_MAY_REPEAT
@ PARAM_MAY_REPEAT
Definition: vf_drawvg.c:222
vgs_parse_color
static int vgs_parse_color(void *log_ctx, struct VGSArgument *arg, const struct VGSParser *parser, const struct VGSParserToken *token)
Definition: vf_drawvg.c:744
CMD_SET_VAR
@ CMD_SET_VAR
setvar (varname value)
Definition: vf_drawvg.c:186
source
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a source
Definition: filter_design.txt:256
VGSParser::source
const char * source
Definition: vf_drawvg.c:451
VGSConstant::value
int value
Definition: vf_drawvg.c:200
cairo_get_current_point
void cairo_get_current_point(cairo_t *cr, double *x, double *y)
Definition: drawvg.c:166
eval.h
AVFILTERPAD_FLAG_NEEDS_WRITABLE
#define AVFILTERPAD_FLAG_NEEDS_WRITABLE
The filter expects writable frames from its input link, duplicating data buffers if needed.
Definition: filters.h:58
VGSCommandSpec::name
const char * name
Definition: vf_drawvg.c:250
VGSConstant::name
const char * name
Definition: vf_drawvg.c:199
relative
static IPT relative(const CmsCtx *ctx, IPT ipt)
Definition: cms.c:544
CMD_HORZ_REL
@ CMD_HORZ_REL
h (dx)
Definition: vf_drawvg.c:152
VGSParserToken::TOKEN_LITERAL
@ TOKEN_LITERAL
Definition: vf_drawvg.c:470
DrawVGContext::time_start
double time_start
Time in seconds of the first frame.
Definition: vf_drawvg.c:2615
VGSCommandSpec::cmd
enum VGSCommand cmd
Definition: vf_drawvg.c:251
init
int(* init)(AVBSFContext *ctx)
Definition: dts2pts.c:550
VGSEvalState::RCP_VALID
@ RCP_VALID
Definition: vf_drawvg.c:1462
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
VGSParameter::PARAM_RAW_IDENT
@ PARAM_RAW_IDENT
Definition: vf_drawvg.c:229
VGSEvalState::interrupted
int interrupted
Register if break was called in a subprogram.
Definition: vf_drawvg.c:1425
height
#define height
Definition: dsp.h:89
V
#define V
Definition: vf_drawvg.c:263
for
for(k=2;k<=8;++k)
Definition: h264pred_template.c:424
VGSParser::proc_names_count
int proc_names_count
Definition: vf_drawvg.c:455
VGSCommandSpec::params
const struct VGSParameter * params
Definition: vf_drawvg.c:252
hypot
static av_const double hypot(double x, double y)
Definition: libm.h:368
CMD_ELLIPSE
@ CMD_ELLIPSE
ellipse (cx cy rx ry)
Definition: vf_drawvg.c:147
drawvg_filter_frame
static int drawvg_filter_frame(AVFilterLink *inlink, AVFrame *frame)
Definition: vf_drawvg.c:2683
TS2T
#define TS2T(ts, tb)
Definition: filters.h:482
VGSParameter::PARAM_NUMERIC_COLOR
@ PARAM_NUMERIC_COLOR
Definition: vf_drawvg.c:224
header
static const uint8_t header[24]
Definition: sdr2.c:68
vgs_eval_state_init
static int vgs_eval_state_init(struct VGSEvalState *state, const struct VGSProgram *program, void *log_ctx, AVFrame *frame)
Definition: vf_drawvg.c:1627
AV_PIX_FMT_RGB32
#define AV_PIX_FMT_RGB32
Definition: pixfmt.h:511
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
VGSEvalState::cairo_ctx
cairo_t * cairo_ctx
Cairo context for drawing operations.
Definition: vf_drawvg.c:1419
state
static struct @548 state
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
line
Definition: graph2dot.c:48
vgs_consts_line_cap
static const struct VGSConstant vgs_consts_line_cap[]
Definition: vf_drawvg.c:203
VGSParserToken::lexeme
const char * lexeme
Definition: vf_drawvg.c:475
M_PI
#define M_PI
Definition: mathematics.h:67
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:221
CMD_SET_COLOR
@ CMD_SET_COLOR
setcolor (color)
Definition: vf_drawvg.c:178
AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
#define AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
Some filters support a generic "enable" expression option that can be used to enable or disable a fil...
Definition: avfilter.h:197
CMD_RADIAL_GRAD
@ CMD_RADIAL_GRAD
radialgrad (cx0 cy0 radius0 cx1 cy1 radius1)
Definition: vf_drawvg.c:166
VGSStatement::cmd
enum VGSCommand cmd
Definition: vf_drawvg.c:676
CMD_PRESERVE
@ CMD_PRESERVE
preserve
Definition: vf_drawvg.c:160
vgs_cmd_change_path
static int vgs_cmd_change_path(enum VGSCommand cmd)
Return 1 if the command changes the current path in the cairo context.
Definition: vf_drawvg.c:379
uninit
static void uninit(AVBSFContext *ctx)
Definition: pcm_rechunk.c:68
VGSParameter::PARAM_VARIADIC
@ PARAM_VARIADIC
Definition: vf_drawvg.c:231
CMD_S_CURVE_TO_REL
@ CMD_S_CURVE_TO_REL
s (dx2 dy2 dx dy)
Definition: vf_drawvg.c:189
VGSProgram::statements
struct VGSStatement * statements
Definition: vf_drawvg.c:682
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
VGSParameter::PARAM_END
@ PARAM_END
Definition: vf_drawvg.c:221
vgs_fn_p
static double vgs_fn_p(void *, double, double)
Function p(x, y) for av_expr_eval.
Definition: vf_drawvg.c:1563
internal.h
CMD_SCALE
@ CMD_SCALE
scale (s)
Definition: vf_drawvg.c:176
VGSEvalState::RCP_UPDATED
@ RCP_UPDATED
Definition: vf_drawvg.c:1462
vsnprintf
#define vsnprintf
Definition: snprintf.h:36
VGSEvalState::frame
AVFrame * frame
Current frame.
Definition: vf_drawvg.c:1416
CMD_NEW_PATH
@ CMD_NEW_PATH
newpath
Definition: vf_drawvg.c:159
av_always_inline
#define av_always_inline
Definition: attributes.h:63
value
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default value
Definition: writing_filters.txt:86
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
CMD_GET_METADATA
@ CMD_GET_METADATA
getmetadata varname key
Definition: vf_drawvg.c:150
AV_PIX_FMT_X2RGB10
#define AV_PIX_FMT_X2RGB10
Definition: pixfmt.h:613
USER_VAR_COUNT
#define USER_VAR_COUNT
Number of user variables that can be created with setvar.
Definition: vf_drawvg.c:76
VGSStatement
Program statements.
Definition: vf_drawvg.c:675
CMD_VERT
@ CMD_VERT
V (y)
Definition: vf_drawvg.c:193
len
int len
Definition: vorbis_enc_data.h:426
CMD_LINEAR_GRAD
@ CMD_LINEAR_GRAD
lineargrad (x0 y0 x1 y1)
Definition: vf_drawvg.c:154
AVFilterPad::name
const char * name
Pad name.
Definition: filters.h:45
VGSProcedure
Block assigned to a procedure by a call to the proc command.
Definition: vf_drawvg.c:1402
draw_quad_curve_to
static void draw_quad_curve_to(struct VGSEvalState *state, int relative, double x1, double y1, double x, double y)
Draw a quadratic bezier from the current point to x, y, The control point is specified by x1,...
Definition: vf_drawvg.c:1695
DrawVGContext::program
struct VGSProgram program
Definition: vf_drawvg.c:2623
CMD_RESET_DASH
@ CMD_RESET_DASH
resetdash
Definition: vf_drawvg.c:170
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
VGSArgument::ARG_METADATA
@ ARG_METADATA
Definition: vf_drawvg.c:655
VGSEvalState::components
uint8_t components[4]
Definition: vf_drawvg.c:1449
VGSParameter::PARAM_NUMERIC
@ PARAM_NUMERIC
Definition: vf_drawvg.c:223
ff_sfc64_get
static uint64_t ff_sfc64_get(FFSFC64 *s)
Definition: sfc64.h:41
AV_PIX_FMT_RGB565
#define AV_PIX_FMT_RGB565
Definition: pixfmt.h:526
CMD_HORZ
@ CMD_HORZ
H (x)
Definition: vf_drawvg.c:151
VAR_TS
@ VAR_TS
Time in seconds of the first frame.
Definition: vf_drawvg.c:61
CMD_SET_LINE_JOIN
@ CMD_SET_LINE_JOIN
setlinejoin (join)
Definition: vf_drawvg.c:183
ret
ret
Definition: filter_design.txt:187
CMD_CURVE_TO
@ CMD_CURVE_TO
C, curveto (x1 y1 x2 y2 x y)
Definition: vf_drawvg.c:143
vgs_func2_names
static const char *const vgs_func2_names[]
Definition: vf_drawvg.c:111
bswap.h
VGSParameter::PARAM_COLOR
@ PARAM_COLOR
Definition: vf_drawvg.c:219
VGSEvalState::rcp
struct VGSEvalState::@393 rcp
drawvg_pix_fmts
static enum AVPixelFormat drawvg_pix_fmts[]
Definition: vf_drawvg.c:2650
FFSWAP
#define FFSWAP(type, a, b)
Definition: macros.h:52
VGSArgument::ARG_SUBPROGRAM
@ ARG_SUBPROGRAM
Definition: vf_drawvg.c:657
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:265
av_strtod
double av_strtod(const char *numstr, char **tail)
Parse the string in numstr and return its value as a double.
Definition: eval.c:107
AV_PIX_FMT_0RGB32
#define AV_PIX_FMT_0RGB32
Definition: pixfmt.h:515
CMD_SAVE
@ CMD_SAVE
save
Definition: vf_drawvg.c:175
av_malloc
void * av_malloc(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:98
CMD_SET_LINE_WIDTH
@ CMD_SET_LINE_WIDTH
setlinewidth (width)
Definition: vf_drawvg.c:184
vgs_eval_state_free
static void vgs_eval_state_free(struct VGSEvalState *state)
Definition: vf_drawvg.c:1658
CMD_SET_DASH_OFFSET
@ CMD_SET_DASH_OFFSET
setdashoffset (offset)
Definition: vf_drawvg.c:180
CMD_T_CURVE_TO
@ CMD_T_CURVE_TO
T (x y)
Definition: vf_drawvg.c:191
CMD_ROUNDEDRECT
@ CMD_ROUNDEDRECT
roundedrect (x y width height radius)
Definition: vf_drawvg.c:174
VGSArgument::metadata
char * metadata
Definition: vf_drawvg.c:671
av_printf_format
static av_printf_format(4, 5)
Definition: vf_drawvg.c:510
VGSParameter::PARAM_PROC_PARAMS
@ PARAM_PROC_PARAMS
Definition: vf_drawvg.c:228
DrawVGContext
Definition: vf_drawvg.c:2608
VGSStatement::args_count
int args_count
Definition: vf_drawvg.c:678
VGSCommandSpec
Definition: vf_drawvg.c:249
CMD_FILL
@ CMD_FILL
fill
Definition: vf_drawvg.c:148
VGSEvalState::cubic_x
double cubic_x
Definition: vf_drawvg.c:1464
VGSProcedure::program
const struct VGSProgram * program
Definition: vf_drawvg.c:1403
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
CMD_DEF_HSLA
@ CMD_DEF_HSLA
defhsla (varname h s l a)
Definition: vf_drawvg.c:144
avfilter.h
format_map
const FormatMap format_map[]
Definition: hwcontext_amf.c:114
VGSParser::var_names
const char * var_names[VAR_COUNT+1]
Definition: vf_drawvg.c:462
VAR_I
@ VAR_I
Loop counter, to use with repeat {}.
Definition: vf_drawvg.c:67
VAR_W
@ VAR_W
Frame width.
Definition: vf_drawvg.c:62
VGSEvalState::quad_y
double quad_y
Definition: vf_drawvg.c:1467
VGSArgument::color
cairo_color * color
Definition: vf_drawvg.c:662
cairo_status
cairo_status_t cairo_status(cairo_t *cr)
Definition: drawvg.c:142
AVFilterContext
An instance of a filter.
Definition: avfilter.h:274
CMD_TRANSLATE
@ CMD_TRANSLATE
translate (tx ty)
Definition: vf_drawvg.c:190
desc
const char * desc
Definition: libsvtav1.c:78
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
FFFilter::p
AVFilter p
The public AVFilter.
Definition: filters.h:270
mem.h
vgs_commands
static const struct VGSCommandSpec vgs_commands[]
Definition: vf_drawvg.c:270
vgs_parse_statement
static int vgs_parse_statement(void *log_ctx, struct VGSParser *parser, struct VGSProgram *program, const struct VGSCommandSpec *decl)
Extract the arguments for a command, and add a new statement to the program.
Definition: vf_drawvg.c:957
CMD_RESTORE
@ CMD_RESTORE
restore
Definition: vf_drawvg.c:172
VGSEvalState
Definition: vf_drawvg.c:1412
MAX_PROC_ARGS
#define MAX_PROC_ARGS
Definition: vf_drawvg.c:245
AVPixFmtDescriptor
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:69
CMD_CLIP_EO
@ CMD_CLIP_EO
eoclip
Definition: vf_drawvg.c:140
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
AVDictionaryEntry
Definition: dict.h:90
vgs_token_span
static void vgs_token_span(const struct VGSParser *parser, const struct VGSParserToken *token, size_t *line, size_t *column)
Compute the line/column numbers of the given token.
Definition: vf_drawvg.c:487
CMD_PROC_CALL
@ CMD_PROC_CALL
call name (expr)*
Definition: vf_drawvg.c:163
VGSArgument::expr
AVExpr * expr
Definition: vf_drawvg.c:664
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
AVERROR_BUG
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:52
MAY_PRESERVE
#define MAY_PRESERVE(funcname)
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
C
#define C(c)
Definition: vf_drawvg.c:265
ASSERT_ARGS
#define ASSERT_ARGS(n)
VGSEvalState::proc_names
const char *const * proc_names
Reference to the procedure names in the VGSProgram.
Definition: vf_drawvg.c:1435
VGSParameter::PARAM_SUBPROGRAM
@ PARAM_SUBPROGRAM
Definition: vf_drawvg.c:230
h
h
Definition: vp9dsp_template.c:2070
VGSParameter::constants
const struct VGSConstant * constants
Array for PARAM_CONSTANT.
Definition: vf_drawvg.c:235
RANDOM_STATES
#define RANDOM_STATES
Number of different states for the randomg function.
Definition: vf_drawvg.c:1399
avstring.h
P
#define P
Definition: vf_drawvg.c:264
width
#define width
Definition: dsp.h:89
R
#define R(...)
Definition: vf_drawvg.c:258
CMD_SET_HSLA
@ CMD_SET_HSLA
sethsla (h s l a)
Definition: vf_drawvg.c:181
av_strndup
char * av_strndup(const char *s, size_t len)
Duplicate a substring of a string.
Definition: mem.c:284
draw_rounded_rect
static void draw_rounded_rect(cairo_t *c, double x, double y, double width, double height, double radius)
Definition: vf_drawvg.c:1791
hsl2rgb
static void hsl2rgb(double h, double s, double l, double *pr, double *pg, double *pb)
Definition: vf_drawvg.c:1809
VGSEvalState::color_vars
cairo_color color_vars[USER_VAR_COUNT]
Colors stored in variables.
Definition: vf_drawvg.c:1444
DrawVGContext::cairo_format
cairo_format_t cairo_format
Equivalent to AVPixelFormat.
Definition: vf_drawvg.c:2612
CMD_LINE_TO
@ CMD_LINE_TO
L, lineto (x y)
Definition: vf_drawvg.c:155
vgs_parser_init
static void vgs_parser_init(struct VGSParser *parser, const char *source)
Definition: vf_drawvg.c:1294
vgs_func1_names
static const char *const vgs_func1_names[]
Definition: vf_drawvg.c:96
snprintf
#define snprintf
Definition: snprintf.h:34
VGSArgument::ARG_COLOR
@ ARG_COLOR
Definition: vf_drawvg.c:651
VGSEvalState::log_ctx
void * log_ctx
Definition: vf_drawvg.c:1413
VGSProcedure::proc_args_count
int proc_args_count
Number of expected arguments.
Definition: vf_drawvg.c:1406
src
#define src
Definition: vp8dsp.c:248
line
The official guide to swscale for confused that consecutive non overlapping rectangles of slice_bottom special converter These generally are unscaled converters of common like for each output line the vertical scaler pulls lines from a ring buffer When the ring buffer does not contain the wanted line
Definition: swscale.txt:40
av_clipd
av_clipd
Definition: af_crystalizer.c:132
av_get_pix_fmt_name
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition: pixdesc.c:3376
draw_cubic_curve_to
static void draw_cubic_curve_to(struct VGSEvalState *state, int relative, double x1, double y1, double x2, double y2, double x, double y)
Similar to quad_curve_to, but for cubic curves.
Definition: vf_drawvg.c:1744
VGSParserToken::position
size_t position
Definition: vf_drawvg.c:476
CMD_CLOSE_PATH
@ CMD_CLOSE_PATH
Z, z, closepath.
Definition: vf_drawvg.c:141
VGSParameter
Definition: vf_drawvg.c:217