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