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