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 
20 // Prevent the `dllimport` attribute in the functions declared by Cairo when
21 // the test is built on a Windows machine.
22 //
23 // This is needed to avoid the "redeclared without dllimport attribute after
24 // being referenced with dll linkage" warnings on every function redefined by
25 // the `MOCK_FN_n` macros below.
26 #define CAIRO_WIN32_STATIC_BUILD
27 
28 #include <cairo.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 
32 #include "libavutil/log.h"
33 #include "libavutil/pixdesc.h"
34 
35 static void mock_av_log(void *ptr, int level, const char *fmt, va_list vl) {
36  printf("av_log[%d]: ", level);
37  vprintf(fmt, vl);
38 }
39 
40 #include "libavfilter/vf_drawvg.c"
41 
42 // Mock for cairo functions.
43 //
44 // `MOCK_FN_n` macros define wrappers for functions that only receive `n`
45 // arguments of type `double`.
46 //
47 // `MOCK_FN_I` macro wrap a function that receives a single integer value.
48 
49 struct _cairo {
52 };
53 
54 static void update_current_point(cairo_t *cr, const char *func, double x, double y) {
55  // Update current point only if the function name contains `_to`.
56  if (strstr(func, "_to") == NULL) {
57  return;
58  }
59 
60  if (strstr(func, "_rel_") == NULL) {
61  cr->current_point_x = x;
62  cr->current_point_y = y;
63  } else {
64  cr->current_point_x += x;
65  cr->current_point_y += y;
66  }
67 }
68 
69 #define MOCK_FN_0(func) \
70  void func(cairo_t* cr) { \
71  puts(#func); \
72  }
73 
74 #define MOCK_FN_1(func) \
75  void func(cairo_t* cr, double a0) { \
76  printf(#func " %.1f\n", a0); \
77  }
78 
79 #define MOCK_FN_2(func) \
80  void func(cairo_t* cr, double a0, double a1) { \
81  update_current_point(cr, #func, a0, a1); \
82  printf(#func " %.1f %.1f\n", a0, a1); \
83  }
84 
85 #define MOCK_FN_4(func) \
86  void func(cairo_t* cr, double a0, double a1, double a2, double a3) { \
87  printf(#func " %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3); \
88  }
89 
90 #define MOCK_FN_5(func) \
91  void func(cairo_t* cr, double a0, double a1, double a2, double a3, double a4) { \
92  printf(#func " %.1f %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3, a4); \
93  }
94 
95 #define MOCK_FN_6(func) \
96  void func(cairo_t* cr, double a0, double a1, double a2, double a3, double a4, double a5) { \
97  update_current_point(cr, #func, a4, a5); \
98  printf(#func " %.1f %.1f %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3, a4, a5); \
99  }
100 
101 #define MOCK_FN_I(func, type) \
102  void func(cairo_t* cr, type i) { \
103  printf(#func " %d\n", (int)i); \
104  }
105 
106 MOCK_FN_5(cairo_arc);
107 MOCK_FN_0(cairo_clip);
108 MOCK_FN_0(cairo_clip_preserve);
109 MOCK_FN_0(cairo_close_path);
110 MOCK_FN_6(cairo_curve_to);
111 MOCK_FN_0(cairo_fill);
112 MOCK_FN_0(cairo_fill_preserve);
113 MOCK_FN_0(cairo_identity_matrix);
114 MOCK_FN_2(cairo_line_to);
115 MOCK_FN_2(cairo_move_to);
116 MOCK_FN_0(cairo_new_path);
117 MOCK_FN_0(cairo_new_sub_path);
118 MOCK_FN_4(cairo_rectangle);
119 MOCK_FN_6(cairo_rel_curve_to);
120 MOCK_FN_2(cairo_rel_line_to);
121 MOCK_FN_2(cairo_rel_move_to);
122 MOCK_FN_0(cairo_reset_clip);
123 MOCK_FN_0(cairo_restore);
124 MOCK_FN_1(cairo_rotate);
125 MOCK_FN_0(cairo_save);
126 MOCK_FN_2(cairo_scale);
127 MOCK_FN_I(cairo_set_fill_rule, cairo_fill_rule_t);
128 MOCK_FN_1(cairo_set_font_size);
129 MOCK_FN_I(cairo_set_line_cap, cairo_line_cap_t);
130 MOCK_FN_I(cairo_set_line_join, cairo_line_join_t);
131 MOCK_FN_1(cairo_set_line_width);
132 MOCK_FN_1(cairo_set_miter_limit);
133 MOCK_FN_4(cairo_set_source_rgba);
134 MOCK_FN_0(cairo_stroke);
135 MOCK_FN_0(cairo_stroke_preserve);
136 MOCK_FN_2(cairo_translate);
137 
138 cairo_bool_t cairo_get_dash_count(cairo_t *cr) {
139  return 1;
140 }
141 
142 cairo_status_t cairo_status(cairo_t *cr) {
143  return CAIRO_STATUS_SUCCESS;
144 }
145 
146 void cairo_get_dash(cairo_t *cr, double *dashes, double *offset) {
147  // Return a dummy value to verify that it is included in
148  // the next call to `cairo_set_dash`.
149  *dashes = -1;
150 
151  if (offset)
152  *offset = -2;
153 }
154 
155 void cairo_set_dash(cairo_t *cr, const double *dashes, int num_dashes, double offset) {
156  printf("%s [", __func__);
157  for (int i = 0; i < num_dashes; i++)
158  printf(" %.1f", dashes[i]);
159  printf(" ] %.1f\n", offset);
160 }
161 
162 cairo_bool_t cairo_has_current_point(cairo_t *cr) {
163  return 1;
164 }
165 
166 void cairo_get_current_point(cairo_t *cr, double *x, double *y) {
167  *x = cr->current_point_x;
168  *y = cr->current_point_y;
169 }
170 
171 void cairo_set_source(cairo_t *cr, cairo_pattern_t *source) {
172  int count;
173  double r, g, b, a;
174  double x0, y0, x1, y1, r0, r1;
175 
176  printf("%s", __func__);
177 
178 #define PRINT_COLOR(prefix) \
179  printf(prefix "#%02lx%02lx%02lx%02lx", lround(r*255), lround(g*255), lround(b*255), lround(a*255))
180 
181  switch (cairo_pattern_get_type(source)) {
182  case CAIRO_PATTERN_TYPE_SOLID:
183  cairo_pattern_get_rgba(source, &r, &g, &b, &a);
184  PRINT_COLOR(" ");
185  break;
186 
187  case CAIRO_PATTERN_TYPE_LINEAR:
188  cairo_pattern_get_linear_points(source, &x0, &y0, &x1, &y1);
189  printf(" lineargrad(%.1f %.1f %.1f %.1f)", x0, y0, x1, y1);
190  break;
191 
192  case CAIRO_PATTERN_TYPE_RADIAL:
193  cairo_pattern_get_radial_circles(source, &x0, &y0, &r0, &x1, &y1, &r1);
194  printf(" radialgrad(%.1f %.1f %.1f %.1f %.1f %.1f)", x0, y0, r0, x1, y1, r1);
195  break;
196  }
197 
198  if (cairo_pattern_get_color_stop_count(source, &count) == CAIRO_STATUS_SUCCESS) {
199  for (int i = 0; i < count; i++) {
200  cairo_pattern_get_color_stop_rgba(source, i, &x0, &r, &g, &b, &a);
201  printf(" %.1f/", x0);
202  PRINT_COLOR("");
203  }
204  }
205 
206  printf("\n");
207 }
208 
209 // Verify that the `vgs_commands` array is sorted, so it can
210 // be used with `bsearch(3)`.
211 static void check_sorted_cmds_array(void) {
212  int failures = 0;
213 
214  for (int i = 0; i < FF_ARRAY_ELEMS(vgs_commands) - 1; i++) {
216  printf("%s: comparator must return 0 for item %d\n", __func__, i);
217  failures++;
218  }
219 
220  if (vgs_comp_command_spec(&vgs_commands[i], &vgs_commands[i + 1]) >= 0) {
221  printf("%s: entry for '%s' must appear after '%s', at index %d\n",
222  __func__, vgs_commands[i].name, vgs_commands[i + 1].name, i);
223  failures++;
224  }
225  }
226 
227  printf("%s: %d failures\n", __func__, failures);
228 }
229 
230 // Compile and run a script.
231 static void check_script(int is_file, const char* source) {
232  int ret;
233 
235 
236  struct VGSEvalState state;
237  struct VGSParser parser;
238  struct VGSProgram program;
239 
240  struct _cairo cairo_ctx = { 0, 0 };
241 
242  if (is_file) {
243  uint8_t *s = NULL;
244 
245  printf("\n--- %s: %s\n", __func__, av_basename(source));
246 
248  if (ret != 0) {
249  printf("Failed to read %s: %d\n", source, ret);
250  return;
251  }
252 
253  source = s;
254  } else {
255  printf("\n--- %s: %s\n", __func__, source);
256  }
257 
258  ret = av_dict_parse_string(&metadata, "m.a=1:m.b=2", "=", ":", 0);
259  av_assert0(ret == 0);
260 
261  vgs_parser_init(&parser, source);
262 
263  ret = vgs_parse(NULL, &parser, &program, 0);
264 
265  int init_ret = vgs_eval_state_init(&state, &program, NULL, NULL);
266  av_assert0(init_ret == 0);
267 
268  for (int i = 0; i < VAR_COUNT; i++)
269  state.vars[i] = 1 << i;
270 
271  vgs_parser_free(&parser);
272 
273  if (ret != 0) {
274  printf("%s: vgs_parse = %d\n", __func__, ret);
275  goto exit;
276  }
277 
278  state.metadata = metadata;
279  state.cairo_ctx = &cairo_ctx;
280 
281  ret = vgs_eval(&state, &program);
283 
284  if (ret != 0)
285  printf("%s: vgs_eval = %d\n", __func__, ret);
286 
287 exit:
289 
290  if (is_file)
291  av_free((void*)source);
292 
293  vgs_free(&program);
294 }
295 
296 int main(int argc, const char **argv)
297 {
298  char buf[512];
299 
301 
303 
304  for (int i = 1; i < argc; i++)
305  check_script(1, argv[i]);
306 
307  // Detect unclosed expressions.
308  check_script(0, "M 0 (1*(t+1)");
309 
310  // Invalid command.
311  check_script(0, "save invalid 1 2");
312 
313  // Invalid constant.
314  check_script(0, "setlinecap unknown m 10 20");
315 
316  // Missing arguments.
317  check_script(0, "M 0 1 2");
318 
319  // Invalid variable names.
320  check_script(0, "setvar ba^d 0");
321 
322  // Reserved names.
323  check_script(0, "setvar cx 0");
324 
325  // Max number of user variables.
326  memset(buf, 0, sizeof(buf));
327  for (int i = 0; i < USER_VAR_COUNT; i++) {
328  av_strlcatf(buf, sizeof(buf), " setvar v%d %d", i, i);
329  }
330  av_strlcatf(buf, sizeof(buf), " M (v0) (v%d) 1 (unknown_var)", USER_VAR_COUNT - 1);
331  check_script(0, buf);
332 
333  // Too many variables.
334  memset(buf, 0, sizeof(buf));
335  for (int i = 0; i < USER_VAR_COUNT + 1; i++) {
336  av_strlcatf(buf, sizeof(buf), " setvar v%d %d", i + 1, i);
337  }
338  check_script(0, buf);
339 
340  // Invalid procedure names.
341  check_script(0, "call a");
342  check_script(0, "proc a { call b } call a");
343 
344  // Invalid arguments list.
345  check_script(0, "proc p0 a1 a2 a3 a4 a5 a6 a7 a8 { break }");
346  check_script(0, "proc p0 a1 a2 { break } call p0 break");
347  check_script(0, "proc p0 a1 a2 { break } call p0 1 2 3");
348 
349  // Long expressions.
350  memset(buf, 0, sizeof(buf));
351  strncat(buf, "M 0 (1", sizeof(buf) - 1);
352  for (int i = 0; i < 100; i++) {
353  strncat(buf, " + n", sizeof(buf) - 1);
354  }
355  strncat(buf, ")", sizeof(buf) - 1);
356  check_script(0, buf);
357 
358  return 0;
359 }
func
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:66
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:155
vgs_eval
static int vgs_eval(struct VGSEvalState *state, const struct VGSProgram *program)
Interpreter for VGSProgram.
Definition: vf_drawvg.c:1883
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:349
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:85
vgs_parser_free
static void vgs_parser_free(struct VGSParser *parser)
Definition: vf_drawvg.c:1306
vf_drawvg.c
cairo_get_dash_count
cairo_bool_t cairo_get_dash_count(cairo_t *cr)
Definition: drawvg.c:138
VGSProgram
Definition: vf_drawvg.c:681
MOCK_FN_6
#define MOCK_FN_6(func)
Definition: drawvg.c:95
VAR_COUNT
#define VAR_COUNT
Total number of variables (default- and user-variables).
Definition: vf_drawvg.c:79
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:54
cairo_set_source
void cairo_set_source(cairo_t *cr, cairo_pattern_t *source)
Definition: drawvg.c:171
vgs_free
static void vgs_free(struct VGSProgram *program)
Release the memory allocated by the program.
Definition: vf_drawvg.c:727
_cairo::current_point_x
double current_point_x
Definition: drawvg.c:50
PRINT_COLOR
#define PRINT_COLOR(prefix)
main
int main(int argc, const char **argv)
Definition: drawvg.c:296
cairo_has_current_point
cairo_bool_t cairo_has_current_point(cairo_t *cr)
Definition: drawvg.c:162
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:146
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:1325
VGSParser
Definition: vf_drawvg.c:450
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:166
av_log_set_callback
void av_log_set_callback(void(*callback)(void *, int, const char *, va_list))
Set the logging callback.
Definition: log.c:492
MOCK_FN_5
#define MOCK_FN_5(func)
Definition: drawvg.c:90
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:1627
_cairo::current_point_y
double current_point_y
Definition: drawvg.c:51
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
state
static struct @548 state
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:69
MOCK_FN_2
#define MOCK_FN_2(func)
Definition: drawvg.c:79
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:211
USER_VAR_COUNT
#define USER_VAR_COUNT
Number of user variables that can be created with setvar.
Definition: vf_drawvg.c:76
ret
ret
Definition: filter_design.txt:187
vgs_eval_state_free
static void vgs_eval_state_free(struct VGSEvalState *state)
Definition: vf_drawvg.c:1658
MOCK_FN_1
#define MOCK_FN_1(func)
Definition: drawvg.c:74
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:49
cairo_status
cairo_status_t cairo_status(cairo_t *cr)
Definition: drawvg.c:142
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:35
vgs_commands
static const struct VGSCommandSpec vgs_commands[]
Definition: vf_drawvg.c:270
VGSEvalState
Definition: vf_drawvg.c:1412
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:231
MOCK_FN_I
#define MOCK_FN_I(func, type)
Definition: drawvg.c:101
vgs_parser_init
static void vgs_parser_init(struct VGSParser *parser, const char *source)
Definition: vf_drawvg.c:1294