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