FFmpeg
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 #include <cairo.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22 
23 #include "libavutil/log.h"
24 #include "libavutil/pixdesc.h"
25 
26 static void mock_av_log(void *ptr, int level, const char *fmt, va_list vl) {
27  printf("av_log[%d]: ", level);
28  vprintf(fmt, vl);
29 }
30 
31 #include "libavfilter/vf_drawvg.c"
32 
33 // Mock for cairo functions.
34 //
35 // `MOCK_FN_n` macros define wrappers for functions that only receive `n`
36 // arguments of type `double`.
37 //
38 // `MOCK_FN_I` macro wrap a function that receives a single integer value.
39 
40 struct _cairo {
43 };
44 
45 static void update_current_point(cairo_t *cr, const char *func, double x, double y) {
46  // Update current point only if the function name contains `_to`.
47  if (strstr(func, "_to") == NULL) {
48  return;
49  }
50 
51  if (strstr(func, "_rel_") == NULL) {
52  cr->current_point_x = x;
53  cr->current_point_y = y;
54  } else {
55  cr->current_point_x += x;
56  cr->current_point_y += y;
57  }
58 }
59 
60 #define MOCK_FN_0(func) \
61  void func(cairo_t* cr) { \
62  puts(#func); \
63  }
64 
65 #define MOCK_FN_1(func) \
66  void func(cairo_t* cr, double a0) { \
67  printf(#func " %.1f\n", a0); \
68  }
69 
70 #define MOCK_FN_2(func) \
71  void func(cairo_t* cr, double a0, double a1) { \
72  update_current_point(cr, #func, a0, a1); \
73  printf(#func " %.1f %.1f\n", a0, a1); \
74  }
75 
76 #define MOCK_FN_4(func) \
77  void func(cairo_t* cr, double a0, double a1, double a2, double a3) { \
78  printf(#func " %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3); \
79  }
80 
81 #define MOCK_FN_5(func) \
82  void func(cairo_t* cr, double a0, double a1, double a2, double a3, double a4) { \
83  printf(#func " %.1f %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3, a4); \
84  }
85 
86 #define MOCK_FN_6(func) \
87  void func(cairo_t* cr, double a0, double a1, double a2, double a3, double a4, double a5) { \
88  update_current_point(cr, #func, a4, a5); \
89  printf(#func " %.1f %.1f %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3, a4, a5); \
90  }
91 
92 #define MOCK_FN_I(func, type) \
93  void func(cairo_t* cr, type i) { \
94  printf(#func " %d\n", (int)i); \
95  }
96 
97 MOCK_FN_5(cairo_arc);
98 MOCK_FN_0(cairo_clip);
99 MOCK_FN_0(cairo_clip_preserve);
100 MOCK_FN_0(cairo_close_path);
101 MOCK_FN_6(cairo_curve_to);
102 MOCK_FN_0(cairo_fill);
103 MOCK_FN_0(cairo_fill_preserve);
104 MOCK_FN_0(cairo_identity_matrix);
105 MOCK_FN_2(cairo_line_to);
106 MOCK_FN_2(cairo_move_to);
107 MOCK_FN_0(cairo_new_path);
108 MOCK_FN_0(cairo_new_sub_path);
109 MOCK_FN_4(cairo_rectangle);
110 MOCK_FN_6(cairo_rel_curve_to);
111 MOCK_FN_2(cairo_rel_line_to);
112 MOCK_FN_2(cairo_rel_move_to);
113 MOCK_FN_0(cairo_reset_clip);
114 MOCK_FN_0(cairo_restore);
115 MOCK_FN_1(cairo_rotate);
116 MOCK_FN_0(cairo_save);
117 MOCK_FN_2(cairo_scale);
118 MOCK_FN_I(cairo_set_fill_rule, cairo_fill_rule_t);
119 MOCK_FN_1(cairo_set_font_size);
120 MOCK_FN_I(cairo_set_line_cap, cairo_line_cap_t);
121 MOCK_FN_I(cairo_set_line_join, cairo_line_join_t);
122 MOCK_FN_1(cairo_set_line_width);
123 MOCK_FN_1(cairo_set_miter_limit);
124 MOCK_FN_4(cairo_set_source_rgba);
125 MOCK_FN_0(cairo_stroke);
126 MOCK_FN_0(cairo_stroke_preserve);
127 MOCK_FN_2(cairo_translate);
128 
129 cairo_bool_t cairo_get_dash_count(cairo_t *cr) {
130  return 1;
131 }
132 
133 cairo_status_t cairo_status(cairo_t *cr) {
134  return CAIRO_STATUS_SUCCESS;
135 }
136 
137 void cairo_get_dash(cairo_t *cr, double *dashes, double *offset) {
138  // Return a dummy value to verify that it is included in
139  // the next call to `cairo_set_dash`.
140  *dashes = -1;
141 
142  if (offset)
143  *offset = -2;
144 }
145 
146 void cairo_set_dash(cairo_t *cr, const double *dashes, int num_dashes, double offset) {
147  printf("%s [", __func__);
148  for (int i = 0; i < num_dashes; i++)
149  printf(" %.1f", dashes[i]);
150  printf(" ] %.1f\n", offset);
151 }
152 
153 cairo_bool_t cairo_has_current_point(cairo_t *cr) {
154  return 1;
155 }
156 
157 void cairo_get_current_point(cairo_t *cr, double *x, double *y) {
158  *x = cr->current_point_x;
159  *y = cr->current_point_y;
160 }
161 
162 void cairo_set_source(cairo_t *cr, cairo_pattern_t *source) {
163  int count;
164  double r, g, b, a;
165  double x0, y0, x1, y1, r0, r1;
166 
167  printf("%s", __func__);
168 
169 #define PRINT_COLOR(prefix) \
170  printf(prefix "#%02x%02x%02x%02x", (int)(r*255), (int)(g*255), (int)(b*255), (int)(a*255))
171 
172  switch (cairo_pattern_get_type(source)) {
173  case CAIRO_PATTERN_TYPE_SOLID:
174  cairo_pattern_get_rgba(source, &r, &g, &b, &a);
175  PRINT_COLOR(" ");
176  break;
177 
178  case CAIRO_PATTERN_TYPE_LINEAR:
179  cairo_pattern_get_linear_points(source, &x0, &y0, &x1, &y1);
180  printf(" lineargrad(%.1f %.1f %.1f %.1f)", x0, y0, x1, y1);
181  break;
182 
183  case CAIRO_PATTERN_TYPE_RADIAL:
184  cairo_pattern_get_radial_circles(source, &x0, &y0, &r0, &x1, &y1, &r1);
185  printf(" radialgrad(%.1f %.1f %.1f %.1f %.1f %.1f)", x0, y0, r0, x1, y1, r1);
186  break;
187  }
188 
189  if (cairo_pattern_get_color_stop_count(source, &count) == CAIRO_STATUS_SUCCESS) {
190  for (int i = 0; i < count; i++) {
191  cairo_pattern_get_color_stop_rgba(source, i, &x0, &r, &g, &b, &a);
192  printf(" %.1f/", x0);
193  PRINT_COLOR("");
194  }
195  }
196 
197  printf("\n");
198 }
199 
200 // Verify that the `vgs_commands` array is sorted, so it can
201 // be used with `bsearch(3)`.
202 static void check_sorted_cmds_array(void) {
203  int failures = 0;
204 
205  for (int i = 0; i < FF_ARRAY_ELEMS(vgs_commands) - 1; i++) {
207  printf("%s: comparator must return 0 for item %d\n", __func__, i);
208  failures++;
209  }
210 
211  if (vgs_comp_command_spec(&vgs_commands[i], &vgs_commands[i + 1]) >= 0) {
212  printf("%s: entry for '%s' must appear after '%s', at index %d\n",
213  __func__, vgs_commands[i].name, vgs_commands[i + 1].name, i);
214  failures++;
215  }
216  }
217 
218  printf("%s: %d failures\n", __func__, failures);
219 }
220 
221 // Compile and run a script.
222 static void check_script(int is_file, const char* source) {
223  int ret;
224 
226 
227  struct VGSEvalState state;
228  struct VGSParser parser;
229  struct VGSProgram program;
230 
231  struct _cairo cairo_ctx = { 0, 0 };
232 
233  if (is_file) {
234  uint8_t *s = NULL;
235 
236  printf("\n--- %s: %s\n", __func__, av_basename(source));
237 
239  if (ret != 0) {
240  printf("Failed to read %s: %d\n", source, ret);
241  return;
242  }
243 
244  source = s;
245  } else {
246  printf("\n--- %s: %s\n", __func__, source);
247  }
248 
249  ret = av_dict_parse_string(&metadata, "m.a=1:m.b=2", "=", ":", 0);
250  av_assert0(ret == 0);
251 
252  vgs_parser_init(&parser, source);
253 
254  ret = vgs_parse(NULL, &parser, &program, 0);
255 
256  int init_ret = vgs_eval_state_init(&state, &program, NULL, NULL);
257  av_assert0(init_ret == 0);
258 
259  for (int i = 0; i < VAR_COUNT; i++)
260  state.vars[i] = 1 << i;
261 
262  vgs_parser_free(&parser);
263 
264  if (ret != 0) {
265  printf("%s: vgs_parse = %d\n", __func__, ret);
266  goto exit;
267  }
268 
269  state.metadata = metadata;
270  state.cairo_ctx = &cairo_ctx;
271 
272  ret = vgs_eval(&state, &program);
274 
275  if (ret != 0)
276  printf("%s: vgs_eval = %d\n", __func__, ret);
277 
278 exit:
280 
281  if (is_file)
282  av_free((void*)source);
283 
284  vgs_free(&program);
285 }
286 
287 int main(int argc, const char **argv)
288 {
289  char buf[512];
290 
292 
294 
295  for (int i = 1; i < argc; i++)
296  check_script(1, argv[i]);
297 
298  // Detect unclosed expressions.
299  check_script(0, "M 0 (1*(t+1)");
300 
301  // Invalid command.
302  check_script(0, "save invalid 1 2");
303 
304  // Invalid constant.
305  check_script(0, "setlinecap unknown m 10 20");
306 
307  // Missing arguments.
308  check_script(0, "M 0 1 2");
309 
310  // Invalid variable names.
311  check_script(0, "setvar ba^d 0");
312 
313  // Reserved names.
314  check_script(0, "setvar cx 0");
315 
316  // Max number of user variables.
317  memset(buf, 0, sizeof(buf));
318  for (int i = 0; i < USER_VAR_COUNT; i++) {
319  av_strlcatf(buf, sizeof(buf), " setvar v%d %d", i, i);
320  }
321  av_strlcatf(buf, sizeof(buf), " M (v0) (v%d) 1 (unknown_var)", USER_VAR_COUNT - 1);
322  check_script(0, buf);
323 
324  // Too many variables.
325  memset(buf, 0, sizeof(buf));
326  for (int i = 0; i < USER_VAR_COUNT + 1; i++) {
327  av_strlcatf(buf, sizeof(buf), " setvar v%d %d", i + 1, i);
328  }
329  check_script(0, buf);
330 
331  // Invalid procedure names.
332  check_script(0, "call a");
333  check_script(0, "proc a { call b } call a");
334 
335  // Invalid arguments list.
336  check_script(0, "proc p0 a1 a2 a3 a4 a5 a6 a7 a8 { break }");
337  check_script(0, "proc p0 a1 a2 { break } call p0 break");
338  check_script(0, "proc p0 a1 a2 { break } call p0 1 2 3");
339 
340  // Long expressions.
341  memset(buf, 0, sizeof(buf));
342  strncat(buf, "M 0 (1", sizeof(buf) - 1);
343  for (int i = 0; i < 100; i++) {
344  strncat(buf, " + n", sizeof(buf) - 1);
345  }
346  strncat(buf, ")", sizeof(buf) - 1);
347  check_script(0, buf);
348 
349  return 0;
350 }
func
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:68
name
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
level
uint8_t level
Definition: svq3.c:208
program
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C program
Definition: undefined.txt:6
r
const char * r
Definition: vf_curves.c:127
printf
__device__ int printf(const char *,...)
cairo_set_dash
void cairo_set_dash(cairo_t *cr, const double *dashes, int num_dashes, double offset)
Definition: drawvg.c:146
state
static struct @539 state
vgs_eval
static int vgs_eval(struct VGSEvalState *state, const struct VGSProgram *program)
Interpreter for VGSProgram.
Definition: vf_drawvg.c:1785
pixdesc.h
vgs_comp_command_spec
static int vgs_comp_command_spec(const void *cs1, const void *cs2)
Comparator for VGSCommandDecl, to be used with bsearch(3).
Definition: vf_drawvg.c:347
b
#define b
Definition: input.c:42
AVDictionary
Definition: dict.c:32
av_strlcatf
size_t av_strlcatf(char *dst, size_t size, const char *fmt,...)
Definition: avstring.c:103
av_basename
const char * av_basename(const char *path)
Thread safe basename.
Definition: avstring.c:253
MOCK_FN_4
#define MOCK_FN_4(func)
Definition: drawvg.c:76
vgs_parser_free
static void vgs_parser_free(struct VGSParser *parser)
Definition: vf_drawvg.c:1241
vf_drawvg.c
cairo_get_dash_count
cairo_bool_t cairo_get_dash_count(cairo_t *cr)
Definition: drawvg.c:129
VGSProgram
Definition: vf_drawvg.c:664
MOCK_FN_6
#define MOCK_FN_6(func)
Definition: drawvg.c:86
VAR_COUNT
#define VAR_COUNT
Total number of variables (default- and user-variables).
Definition: vf_drawvg.c:78
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
ff_load_textfile
int ff_load_textfile(void *log_ctx, const char *textfile, unsigned char **text, size_t *text_size)
Definition: textutils.c:354
s
#define s(width, name)
Definition: cbs_vp9.c:198
g
const char * g
Definition: vf_curves.c:128
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:41
update_current_point
static void update_current_point(cairo_t *cr, const char *func, double x, double y)
Definition: drawvg.c:45
cairo_set_source
void cairo_set_source(cairo_t *cr, cairo_pattern_t *source)
Definition: drawvg.c:162
vgs_free
static void vgs_free(struct VGSProgram *program)
Release the memory allocated by the program.
Definition: vf_drawvg.c:706
_cairo::current_point_x
double current_point_x
Definition: drawvg.c:41
PRINT_COLOR
#define PRINT_COLOR(prefix)
main
int main(int argc, const char **argv)
Definition: drawvg.c:287
cairo_has_current_point
cairo_bool_t cairo_has_current_point(cairo_t *cr)
Definition: drawvg.c:153
metadata
Stream codec metadata
Definition: ogg-flac-chained-meta.txt:2
NULL
#define NULL
Definition: coverity.c:32
cairo_get_dash
void cairo_get_dash(cairo_t *cr, double *dashes, double *offset)
Definition: drawvg.c:137
vgs_parse
static int vgs_parse(void *log_ctx, struct VGSParser *parser, struct VGSProgram *program, int subprogram)
Build a program by parsing a script.
Definition: vf_drawvg.c:1260
VGSParser
Definition: vf_drawvg.c:432
source
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a source
Definition: filter_design.txt:256
cairo_get_current_point
void cairo_get_current_point(cairo_t *cr, double *x, double *y)
Definition: drawvg.c:157
av_log_set_callback
void av_log_set_callback(void(*callback)(void *, int, const char *, va_list))
Set the logging callback.
Definition: log.c:490
MOCK_FN_5
#define MOCK_FN_5(func)
Definition: drawvg.c:81
vgs_eval_state_init
static int vgs_eval_state_init(struct VGSEvalState *state, const struct VGSProgram *program, void *log_ctx, AVFrame *frame)
Definition: vf_drawvg.c:1534
_cairo::current_point_y
double current_point_y
Definition: drawvg.c:42
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
av_dict_free
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values.
Definition: dict.c:233
MOCK_FN_0
#define MOCK_FN_0(func)
Definition: drawvg.c:60
MOCK_FN_2
#define MOCK_FN_2(func)
Definition: drawvg.c:70
log.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
check_sorted_cmds_array
static void check_sorted_cmds_array(void)
Definition: drawvg.c:202
USER_VAR_COUNT
#define USER_VAR_COUNT
Number of user variables that can be created with setvar.
Definition: vf_drawvg.c:75
ret
ret
Definition: filter_design.txt:187
vgs_eval_state_free
static void vgs_eval_state_free(struct VGSEvalState *state)
Definition: vf_drawvg.c:1560
MOCK_FN_1
#define MOCK_FN_1(func)
Definition: drawvg.c:65
av_dict_parse_string
int av_dict_parse_string(AVDictionary **pm, const char *str, const char *key_val_sep, const char *pairs_sep, int flags)
Parse the key/value pairs list and add the parsed entries to a dictionary.
Definition: dict.c:210
_cairo
Definition: drawvg.c:40
cairo_status
cairo_status_t cairo_status(cairo_t *cr)
Definition: drawvg.c:133
failures
static int failures
Definition: probetest.c:33
mock_av_log
static void mock_av_log(void *ptr, int level, const char *fmt, va_list vl)
Definition: drawvg.c:26
vgs_commands
static const struct VGSCommandSpec vgs_commands[]
Definition: vf_drawvg.c:268
VGSEvalState
Definition: vf_drawvg.c:1347
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
cr
static double cr(void *priv, double x, double y)
Definition: vf_geq.c:248
check_script
static void check_script(int is_file, const char *source)
Definition: drawvg.c:222
MOCK_FN_I
#define MOCK_FN_I(func, type)
Definition: drawvg.c:92
vgs_parser_init
static void vgs_parser_init(struct VGSParser *parser, const char *source)
Definition: vf_drawvg.c:1229