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