1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-11-23 21:54:53 +02:00

lavfi: add drawvg video filter.

The drawvg filter can draw vector graphics on top of a video, using libcairo. It
is enabled if FFmpeg is configured with `--enable-cairo`.

The language for drawvg scripts is documented in `doc/drawvg-reference.texi`.

There are two new tests:

- `fate-filter-drawvg-interpreter` launch a script with most commands, and
  verify which libcairo functions are executed.
- `fate-filter-drawvg-video` render a very simple image, just to verify that
  libcairo is working as expected.

Signed-off-by: Ayose <ayosec@gmail.com>
This commit is contained in:
Ayose
2025-10-18 17:34:36 +00:00
committed by michaelni
parent d77f917621
commit 016d767c8e
16 changed files with 6170 additions and 0 deletions

View File

@@ -100,6 +100,7 @@ libavfilter/.*f_ebur128.* @haasn
libavfilter/vf_blackdetect.* @haasn
libavfilter/vf_colordetect.* @haasn
libavfilter/vf_colorspace.* @rbultje
libavfilter/.*drawvg.* @ayosec
libavfilter/vf_icc.* @haasn
libavfilter/vf_libplacebo.* @haasn
libavfilter/vf_premultiply.* @haasn
@@ -215,4 +216,5 @@ doc/.* @GyanD
# tests
# =====
tests/checkasm/riscv/.* @Courmisch
tests/ref/.*drawvg.* @ayosec
tests/ref/fate/sub-mcc.* @programmerjake

View File

@@ -4,6 +4,7 @@ releases are sorted from youngest to oldest.
version <next>:
- ffprobe -codec option
- EXIF Metadata Parsing
- drawvg filter (--enable-cairo)
- gfxcapture: Windows.Graphics.Capture based window/monitor capture
- hxvs demuxer for HXVS/HXVT IP camera format
- MPEG-H 3D Audio decoding via mpeghdec

4
configure vendored
View File

@@ -200,6 +200,7 @@ External library support:
--disable-avfoundation disable Apple AVFoundation framework [autodetect]
--enable-avisynth enable reading of AviSynth script files [no]
--disable-bzlib disable bzlib [autodetect]
--enable-cairo enable cairo [no]
--disable-coreimage disable Apple CoreImage framework [autodetect]
--enable-chromaprint enable audio fingerprinting with chromaprint [no]
--enable-frei0r enable frei0r video filtering [no]
@@ -1949,6 +1950,7 @@ EXTERNAL_LIBRARY_LIST="
$EXTERNAL_LIBRARY_NONFREE_LIST
$EXTERNAL_LIBRARY_VERSION3_LIST
$EXTERNAL_LIBRARY_GPLV3_LIST
cairo
chromaprint
gcrypt
gnutls
@@ -4003,6 +4005,7 @@ dnn_detect_filter_select="dnn"
dnn_processing_filter_select="dnn"
drawtext_filter_deps="libfreetype libharfbuzz"
drawtext_filter_suggest="libfontconfig libfribidi"
drawvg_filter_deps="cairo"
elbg_filter_deps="avcodec"
eq_filter_deps="gpl"
erosion_opencl_filter_deps="opencl"
@@ -7082,6 +7085,7 @@ done
enabled avisynth && { require_headers "avisynth/avisynth_c.h avisynth/avs/version.h" &&
{ test_cpp_condition avisynth/avs/version.h "AVS_MAJOR_VER >= 3 && AVS_MINOR_VER >= 7 && AVS_BUGFIX_VER >= 3 || AVS_MAJOR_VER >= 3 && AVS_MINOR_VER > 7 || AVS_MAJOR_VER > 3" ||
die "ERROR: AviSynth+ header version must be >= 3.7.3"; } }
enabled cairo && require_pkg_config cairo cairo "cairo.h" cairo_create
enabled cuda_nvcc && { check_nvcc cuda_nvcc || die "ERROR: failed checking for nvcc."; }
enabled chromaprint && { check_pkg_config chromaprint libchromaprint "chromaprint.h" chromaprint_get_version ||
require chromaprint chromaprint.h chromaprint_get_version -lchromaprint; }

View File

@@ -28,6 +28,7 @@ HTMLPAGES = $(AVPROGS-yes:%=doc/%.html) $(AVPROGS-yes:%=doc/%-all.html) $(COMP
doc/mailing-list-faq.html \
doc/nut.html \
doc/platform.html \
doc/drawvg-reference.html \
$(SRC_PATH)/doc/bootstrap.min.css \
$(SRC_PATH)/doc/style.min.css \
$(SRC_PATH)/doc/default.css \

2772
doc/drawvg-reference.texi Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13049,6 +13049,78 @@ For more information about libfribidi, check:
For more information about libharfbuzz, check:
@url{https://github.com/harfbuzz/harfbuzz}.
@anchor{drawvg}
@section drawvg
Draw vector graphics on top of video frames, by executing a script written in
a custom language called VGS (@emph{Vector Graphics Script}).
The documentation for the language can be found in
@ref{,,drawvg - Language Reference,drawvg-reference}.
Graphics are rendered using the @uref{https://cairographics.org/,cario 2D
graphics library}.
To enable compilation of this filter, you need to configure FFmpeg with
@code{--enable-cairo}.
@subsection Parameters
Either @code{script} or @code{file} must be set.
@table @option
@item s, script
Script source to draw the graphics.
@item file
Path of the file to load the script source.
@end table
@subsection Pixel Formats
Since Cairo only supports RGB images, if the input video is something else (like
YUV 4:2:0), before executing the script the video is converted to a format
compatible with Cairo. Then, you have to use use either the @ref{format} filter,
or the @code{-pix_fmt} option, to convert it to the expected format in the
output.
@subsection Examples
@itemize
@item
Draw the outline of an ellipse.
@example
ffmpeg -i input.webm \
-vf 'drawvg=ellipse (w/2) (h/2) (w/3) (h/3) stroke' \
-pix_fmt yuv420p \
output.webm
@end example
@item
Draw a square rotating in the middle of the frame.
The script for drawvg is in a file @code{draw.vgs}:
@example
translate (w/2) (h/2)
rotate t
rect -100 -100 200 200
setcolor red@@0.5
fill
@end example
Then:
@example
ffmpeg -i input.webm -vf 'drawvg=file=draw.vgs,format=yuv420p' output.webm
@end example
@end itemize
@section edgedetect
Detect and draw edges. The filter uses the Canny Edge Detection algorithm.

View File

@@ -298,6 +298,7 @@ OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
OBJS-$(CONFIG_DRAWGRAPH_FILTER) += f_drawgraph.o
OBJS-$(CONFIG_DRAWGRID_FILTER) += vf_drawbox.o
OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o textutils.o
OBJS-$(CONFIG_DRAWVG_FILTER) += vf_drawvg.o textutils.o
OBJS-$(CONFIG_EDGEDETECT_FILTER) += vf_edgedetect.o edge_common.o
OBJS-$(CONFIG_ELBG_FILTER) += vf_elbg.o
OBJS-$(CONFIG_ENTROPY_FILTER) += vf_entropy.o
@@ -681,6 +682,10 @@ SKIPHEADERS-$(CONFIG_VULKAN) += vulkan_filter.h
TOOLS = graph2dot
TESTPROGS = drawutils filtfmts formats integral
ifdef CONFIG_DRAWVG_FILTER
TESTPROGS += drawvg
endif
TOOLS-$(CONFIG_LIBZMQ) += zmqsend
clean::

View File

@@ -272,6 +272,7 @@ extern const FFFilter ff_vf_drawbox;
extern const FFFilter ff_vf_drawgraph;
extern const FFFilter ff_vf_drawgrid;
extern const FFFilter ff_vf_drawtext;
extern const FFFilter ff_vf_drawvg;
extern const FFFilter ff_vf_edgedetect;
extern const FFFilter ff_vf_elbg;
extern const FFFilter ff_vf_entropy;

View File

@@ -7,6 +7,7 @@
/dnn-layer-avgpool
/dnn-layer-dense
/drawutils
/drawvg
/filtfmts
/formats
/integral

350
libavfilter/tests/drawvg.c Normal file
View File

@@ -0,0 +1,350 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <cairo.h>
#include <stdarg.h>
#include <stdio.h>
#include "libavutil/log.h"
#include "libavutil/pixdesc.h"
static void mock_av_log(void *ptr, int level, const char *fmt, va_list vl) {
printf("av_log[%d]: ", level);
vprintf(fmt, vl);
}
#include "libavfilter/vf_drawvg.c"
// Mock for cairo functions.
//
// `MOCK_FN_n` macros define wrappers for functions that only receive `n`
// arguments of type `double`.
//
// `MOCK_FN_I` macro wrap a function that receives a single integer value.
struct _cairo {
double current_point_x;
double current_point_y;
};
static void update_current_point(cairo_t *cr, const char *func, double x, double y) {
// Update current point only if the function name contains `_to`.
if (strstr(func, "_to") == NULL) {
return;
}
if (strstr(func, "_rel_") == NULL) {
cr->current_point_x = x;
cr->current_point_y = y;
} else {
cr->current_point_x += x;
cr->current_point_y += y;
}
}
#define MOCK_FN_0(func) \
void func(cairo_t* cr) { \
puts(#func); \
}
#define MOCK_FN_1(func) \
void func(cairo_t* cr, double a0) { \
printf(#func " %.1f\n", a0); \
}
#define MOCK_FN_2(func) \
void func(cairo_t* cr, double a0, double a1) { \
update_current_point(cr, #func, a0, a1); \
printf(#func " %.1f %.1f\n", a0, a1); \
}
#define MOCK_FN_4(func) \
void func(cairo_t* cr, double a0, double a1, double a2, double a3) { \
printf(#func " %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3); \
}
#define MOCK_FN_5(func) \
void func(cairo_t* cr, double a0, double a1, double a2, double a3, double a4) { \
printf(#func " %.1f %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3, a4); \
}
#define MOCK_FN_6(func) \
void func(cairo_t* cr, double a0, double a1, double a2, double a3, double a4, double a5) { \
update_current_point(cr, #func, a4, a5); \
printf(#func " %.1f %.1f %.1f %.1f %.1f %.1f\n", a0, a1, a2, a3, a4, a5); \
}
#define MOCK_FN_I(func, type) \
void func(cairo_t* cr, type i) { \
printf(#func " %d\n", (int)i); \
}
MOCK_FN_5(cairo_arc);
MOCK_FN_0(cairo_clip);
MOCK_FN_0(cairo_clip_preserve);
MOCK_FN_0(cairo_close_path);
MOCK_FN_6(cairo_curve_to);
MOCK_FN_0(cairo_fill);
MOCK_FN_0(cairo_fill_preserve);
MOCK_FN_0(cairo_identity_matrix);
MOCK_FN_2(cairo_line_to);
MOCK_FN_2(cairo_move_to);
MOCK_FN_0(cairo_new_path);
MOCK_FN_0(cairo_new_sub_path);
MOCK_FN_4(cairo_rectangle);
MOCK_FN_6(cairo_rel_curve_to);
MOCK_FN_2(cairo_rel_line_to);
MOCK_FN_2(cairo_rel_move_to);
MOCK_FN_0(cairo_reset_clip);
MOCK_FN_0(cairo_restore);
MOCK_FN_1(cairo_rotate);
MOCK_FN_0(cairo_save);
MOCK_FN_2(cairo_scale);
MOCK_FN_I(cairo_set_fill_rule, cairo_fill_rule_t);
MOCK_FN_1(cairo_set_font_size);
MOCK_FN_I(cairo_set_line_cap, cairo_line_cap_t);
MOCK_FN_I(cairo_set_line_join, cairo_line_join_t);
MOCK_FN_1(cairo_set_line_width);
MOCK_FN_1(cairo_set_miter_limit);
MOCK_FN_4(cairo_set_source_rgba);
MOCK_FN_0(cairo_stroke);
MOCK_FN_0(cairo_stroke_preserve);
MOCK_FN_2(cairo_translate);
cairo_bool_t cairo_get_dash_count(cairo_t *cr) {
return 1;
}
cairo_status_t cairo_status(cairo_t *cr) {
return CAIRO_STATUS_SUCCESS;
}
void cairo_get_dash(cairo_t *cr, double *dashes, double *offset) {
// Return a dummy value to verify that it is included in
// the next call to `cairo_set_dash`.
*dashes = -1;
if (offset)
*offset = -2;
}
void cairo_set_dash(cairo_t *cr, const double *dashes, int num_dashes, double offset) {
printf("%s [", __func__);
for (int i = 0; i < num_dashes; i++)
printf(" %.1f", dashes[i]);
printf(" ] %.1f\n", offset);
}
cairo_bool_t cairo_has_current_point(cairo_t *cr) {
return 1;
}
void cairo_get_current_point(cairo_t *cr, double *x, double *y) {
*x = cr->current_point_x;
*y = cr->current_point_y;
}
void cairo_set_source(cairo_t *cr, cairo_pattern_t *source) {
int count;
double r, g, b, a;
double x0, y0, x1, y1, r0, r1;
printf("%s", __func__);
#define PRINT_COLOR(prefix) \
printf(prefix "#%02x%02x%02x%02x", (int)(r*255), (int)(g*255), (int)(b*255), (int)(a*255))
switch (cairo_pattern_get_type(source)) {
case CAIRO_PATTERN_TYPE_SOLID:
cairo_pattern_get_rgba(source, &r, &g, &b, &a);
PRINT_COLOR(" ");
break;
case CAIRO_PATTERN_TYPE_LINEAR:
cairo_pattern_get_linear_points(source, &x0, &y0, &x1, &y1);
printf(" lineargrad(%.1f %.1f %.1f %.1f)", x0, y0, x1, y1);
break;
case CAIRO_PATTERN_TYPE_RADIAL:
cairo_pattern_get_radial_circles(source, &x0, &y0, &r0, &x1, &y1, &r1);
printf(" radialgrad(%.1f %.1f %.1f %.1f %.1f %.1f)", x0, y0, r0, x1, y1, r1);
break;
}
if (cairo_pattern_get_color_stop_count(source, &count) == CAIRO_STATUS_SUCCESS) {
for (int i = 0; i < count; i++) {
cairo_pattern_get_color_stop_rgba(source, i, &x0, &r, &g, &b, &a);
printf(" %.1f/", x0);
PRINT_COLOR("");
}
}
printf("\n");
}
// Verify that the `vgs_commands` array is sorted, so it can
// be used with `bsearch(3)`.
static void check_sorted_cmds_array(void) {
int failures = 0;
for (int i = 0; i < FF_ARRAY_ELEMS(vgs_commands) - 1; i++) {
if (vgs_comp_command_spec(&vgs_commands[i], &vgs_commands[i]) != 0) {
printf("%s: comparator must return 0 for item %d\n", __func__, i);
failures++;
}
if (vgs_comp_command_spec(&vgs_commands[i], &vgs_commands[i + 1]) >= 0) {
printf("%s: entry for '%s' must appear after '%s', at index %d\n",
__func__, vgs_commands[i].name, vgs_commands[i + 1].name, i);
failures++;
}
}
printf("%s: %d failures\n", __func__, failures);
}
// Compile and run a script.
static void check_script(int is_file, const char* source) {
int ret;
AVDictionary *metadata = NULL;
struct VGSEvalState state;
struct VGSParser parser;
struct VGSProgram program;
struct _cairo cairo_ctx = { 0, 0 };
if (is_file) {
uint8_t *s = NULL;
printf("\n--- %s: %s\n", __func__, av_basename(source));
ret = ff_load_textfile(NULL, source, &s, NULL);
if (ret != 0) {
printf("Failed to read %s: %d\n", source, ret);
return;
}
source = s;
} else {
printf("\n--- %s: %s\n", __func__, source);
}
ret = av_dict_parse_string(&metadata, "m.a=1:m.b=2", "=", ":", 0);
av_assert0(ret == 0);
vgs_parser_init(&parser, source);
ret = vgs_parse(NULL, &parser, &program, 0);
int init_ret = vgs_eval_state_init(&state, &program, NULL, NULL);
av_assert0(init_ret == 0);
for (int i = 0; i < VAR_COUNT; i++)
state.vars[i] = 1 << i;
vgs_parser_free(&parser);
if (ret != 0) {
printf("%s: vgs_parse = %d\n", __func__, ret);
goto exit;
}
state.metadata = metadata;
state.cairo_ctx = &cairo_ctx;
ret = vgs_eval(&state, &program);
vgs_eval_state_free(&state);
if (ret != 0)
printf("%s: vgs_eval = %d\n", __func__, ret);
exit:
av_dict_free(&metadata);
if (is_file)
av_free((void*)source);
vgs_free(&program);
}
int main(int argc, const char **argv)
{
char buf[512];
av_log_set_callback(mock_av_log);
check_sorted_cmds_array();
for (int i = 1; i < argc; i++)
check_script(1, argv[i]);
// Detect unclosed expressions.
check_script(0, "M 0 (1*(t+1)");
// Invalid command.
check_script(0, "save invalid 1 2");
// Invalid constant.
check_script(0, "setlinecap unknown m 10 20");
// Missing arguments.
check_script(0, "M 0 1 2");
// Invalid variable names.
check_script(0, "setvar ba^d 0");
// Reserved names.
check_script(0, "setvar cx 0");
// Max number of user variables.
memset(buf, 0, sizeof(buf));
for (int i = 0; i < USER_VAR_COUNT; i++) {
av_strlcatf(buf, sizeof(buf), " setvar v%d %d", i, i);
}
av_strlcatf(buf, sizeof(buf), " M (v0) (v%d) 1 (unknown_var)", USER_VAR_COUNT - 1);
check_script(0, buf);
// Too many variables.
memset(buf, 0, sizeof(buf));
for (int i = 0; i < USER_VAR_COUNT + 1; i++) {
av_strlcatf(buf, sizeof(buf), " setvar v%d %d", i + 1, i);
}
check_script(0, buf);
// Invalid procedure names.
check_script(0, "call a");
check_script(0, "proc a { call b } call a");
// Invalid arguments list.
check_script(0, "proc p0 a1 a2 a3 a4 a5 a6 a7 a8 { break }");
check_script(0, "proc p0 a1 a2 { break } call p0 break");
check_script(0, "proc p0 a1 a2 { break } call p0 1 2 3");
// Long expressions.
memset(buf, 0, sizeof(buf));
strncat(buf, "M 0 (1", sizeof(buf) - 1);
for (int i = 0; i < 100; i++) {
strncat(buf, " + n", sizeof(buf) - 1);
}
strncat(buf, ")", sizeof(buf) - 1);
check_script(0, buf);
return 0;
}

2708
libavfilter/vf_drawvg.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -452,6 +452,13 @@ fate-filter-fps-down-eof-pass: CMD = framecrc -lavfi testsrc2=r=7:d=3.5,fps=3:eo
fate-filter-fps-start-drop: CMD = framecrc -lavfi testsrc2=r=7:d=3.5,fps=3:start_time=1.5
fate-filter-fps-start-fill: CMD = framecrc -lavfi testsrc2=r=7:d=1.5,setpts=PTS+14,fps=3:start_time=1.5
DRAWVG_SCRIPT_ALL = $(SRC_PATH)/tests/ref/lavf/drawvg.all
FATE_FILTER-$(CONFIG_DRAWVG_FILTER) += fate-filter-drawvg-interpreter
fate-filter-drawvg-interpreter: $(DRAWVG_SCRIPT_ALL)
fate-filter-drawvg-interpreter: libavfilter/tests/drawvg$(EXESUF)
fate-filter-drawvg-interpreter: CMD = run libavfilter/tests/drawvg$(EXESUF) $(DRAWVG_SCRIPT_ALL)
FATE_FILTER_SAMPLES-$(call FILTERDEMDEC, FPS SCALE, MOV, QTRLE) += fate-filter-fps-cfr fate-filter-fps
fate-filter-fps-cfr: CMD = framecrc -auto_conversion_filters -i $(TARGET_SAMPLES)/qtrle/apple-animation-variable-fps-bug.mov -r 30 -fps_mode cfr -pix_fmt yuv420p
fate-filter-fps: CMD = framecrc -auto_conversion_filters -i $(TARGET_SAMPLES)/qtrle/apple-animation-variable-fps-bug.mov -vf fps=30 -pix_fmt yuv420p
@@ -602,6 +609,11 @@ fate-filter-tiltandshift-410: CMD = framecrc -c:v pgmyuv -i $(SRC) -flags +bitex
fate-filter-tiltandshift-422: CMD = framecrc -c:v pgmyuv -i $(SRC) -flags +bitexact -vf scale=sws_flags=+accurate_rnd+bitexact,format=yuv422p,tiltandshift
fate-filter-tiltandshift-444: CMD = framecrc -c:v pgmyuv -i $(SRC) -flags +bitexact -vf scale=sws_flags=+accurate_rnd+bitexact,format=yuv444p,tiltandshift
DRAWVG_SCRIPT_LINES = $(SRC_PATH)/tests/ref/lavf/drawvg.lines
FATE_FILTER_VSYNTH_VIDEO_FILTER-$(CONFIG_DRAWVG_FILTER) += fate-filter-drawvg-video
fate-filter-drawvg-video: $(DRAWVG_SCRIPT_LINES)
fate-filter-drawvg-video: CMD = video_filter scale,format=bgr0,drawvg=file=$(DRAWVG_SCRIPT_LINES)
tests/pixfmts.mak: TAG = GEN
tests/pixfmts.mak: ffmpeg$(PROGSSUF)$(EXESUF) | tests
$(M)printf "PIXFMTS = " > $@

View File

@@ -0,0 +1,130 @@
check_sorted_cmds_array: 0 failures
--- check_script: drawvg.all
cairo_set_line_join 0
cairo_set_line_cap 1
cairo_move_to 0.0 0.0
cairo_rel_line_to 0.0 0.0
cairo_rel_line_to 1.0 1.0
cairo_rel_line_to 2.0 2.0
cairo_line_to -1.0 -2.0
av_log[32]: [29:7] 1 = 1.000000 | [29:9] foo = -1.000000 | [29:13] (bar - 2) = -4.000000
cairo_set_source lineargrad(0.0 0.0 1.0 1.0) 0.0/#ff0000ff 1.0/#0000ffff
cairo_save
cairo_restore
cairo_scale 1.0 1.0
cairo_scale 2.0 3.0
cairo_translate 4.0 5.0
cairo_rotate 6.0
cairo_identity_matrix
cairo_rectangle 1.0 2.0 3.0 4.0
cairo_save
cairo_translate 1.0 2.0
cairo_new_sub_path
cairo_arc 0.0 0.0 3.0 0.0 6.3
cairo_close_path
cairo_new_sub_path
cairo_restore
cairo_new_sub_path
cairo_arc 150.0 150.0 50.0 3.1 4.7
cairo_arc 450.0 150.0 50.0 4.7 6.3
cairo_arc 450.0 450.0 50.0 0.0 1.6
cairo_arc 150.0 450.0 50.0 1.6 3.1
cairo_close_path
cairo_set_source #abcdefff
cairo_stroke
cairo_set_source lineargrad(0.0 1.0 2.0 3.0) 0.0/#ff0000ff 1.0/#0000ffff
cairo_stroke
cairo_set_source radialgrad(1.0 2.0 3.0 4.0 5.0 6.0) 0.0/#000000ff 0.1/#ffffffff 0.1/#000000ff 0.2/#ffffffff
cairo_stroke
cairo_move_to 10.0 50.0
cairo_curve_to 20.0 33.3 30.0 33.3 40.0 50.0
cairo_curve_to 50.0 66.7 60.0 66.7 70.0 50.0
cairo_curve_to 80.0 33.3 90.0 33.3 100.0 50.0
cairo_curve_to 120.0 100.0 140.0 0.0 200.0 50.0
cairo_set_fill_rule 0
cairo_fill_preserve
cairo_set_fill_rule 1
cairo_fill_preserve
cairo_stroke_preserve
cairo_set_fill_rule 0
cairo_clip_preserve
cairo_set_fill_rule 0
cairo_fill
cairo_set_fill_rule 1
cairo_fill
cairo_set_fill_rule 0
cairo_clip
cairo_set_fill_rule 1
cairo_clip
cairo_set_dash [ -1.0 1.0 ] -2.0
cairo_set_dash [ -1.0 2.0 ] -2.0
cairo_set_dash [ -1.0 3.0 ] -2.0
cairo_set_dash [ -1.0 ] 4.0
cairo_set_dash [ ] 0.0
cairo_move_to 1.0 2.0
cairo_rel_line_to -1.0 -2.0
cairo_set_source #19334c66
cairo_set_fill_rule 0
cairo_fill
cairo_set_source #475b3d66
cairo_set_fill_rule 0
cairo_fill
cairo_set_source #7f99b2cc
cairo_set_fill_rule 0
cairo_fill
cairo_set_source #a8d7efe5
cairo_set_fill_rule 0
cairo_fill
cairo_rel_line_to 1.0 3.0
cairo_rel_line_to nan 0.0
--- check_script: M 0 (1*(t+1)
av_log[16]: Invalid token '(' at line 1, column 5: Unmatched parenthesis.
check_script: vgs_parse = -22
--- check_script: save invalid 1 2
av_log[16]: Invalid token 'invalid' at line 1, column 6: Expected command.
check_script: vgs_parse = -22
--- check_script: setlinecap unknown m 10 20
av_log[16]: Invalid token 'unknown' at line 1, column 12: Expected one of 'butt' 'round' 'square'.
check_script: vgs_parse = -22
--- check_script: M 0 1 2
av_log[16]: Invalid token '<EOF>' at line 1, column 8: Expected numeric argument.
check_script: vgs_parse = -22
--- check_script: setvar ba^d 0
av_log[16]: Invalid token 'ba^d' at line 1, column 8: Invalid variable name.
check_script: vgs_parse = -22
--- check_script: setvar cx 0
av_log[16]: Invalid token 'cx' at line 1, column 8: Reserved variable name.
check_script: vgs_parse = -22
--- check_script: setvar v0 0 setvar v1 1 setvar v2 2 setvar v3 3 setvar v4 4 setvar v5 5 setvar v6 6 setvar v7 7 setvar v8 8 setvar v9 9 setvar v10 10 setvar v11 11 setvar v12 12 setvar v13 13 setvar v14 14 setvar v15 15 setvar v16 16 setvar v17 17 setvar v18 18 setvar v19 19 M (v0) (v19) 1 (unknown_var)
av_log[16]: Undefined constant or missing '(' in 'unknown_var)'
av_log[16]: Invalid token '(unknown_var)' at line 1, column 277: Invalid expression.
check_script: vgs_parse = -22
--- check_script: setvar v1 0 setvar v2 1 setvar v3 2 setvar v4 3 setvar v5 4 setvar v6 5 setvar v7 6 setvar v8 7 setvar v9 8 setvar v10 9 setvar v11 10 setvar v12 11 setvar v13 12 setvar v14 13 setvar v15 14 setvar v16 15 setvar v17 16 setvar v18 17 setvar v19 18 setvar v20 19 setvar v21 20
av_log[16]: Invalid token 'v21' at line 1, column 270: Too many user variables. Can define up to 20 variables.
check_script: vgs_parse = -22
--- check_script: call a
av_log[16]: Missing body for procedure 'a'
--- check_script: proc a { call b } call a
av_log[16]: Missing body for procedure 'b'
--- check_script: proc p0 a1 a2 a3 a4 a5 a6 a7 a8 { break }
av_log[16]: Invalid token 'a7' at line 1, column 27: Too many parameters. Limit is 6
check_script: vgs_parse = -22
--- check_script: proc p0 a1 a2 { break } call p0 break
av_log[16]: Procedure expects 2 arguments, but received 0.
--- check_script: proc p0 a1 a2 { break } call p0 1 2 3
av_log[16]: Procedure expects 2 arguments, but received 3.
--- check_script: M 0 (1 + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n + n)
cairo_move_to 0.0 101.0

View File

@@ -0,0 +1 @@
drawvg-video caa7642950ab2fb1367bd28c287f31bd

100
tests/ref/lavf/drawvg.all Normal file
View File

@@ -0,0 +1,100 @@
// Script to test how drawvg instructions are translated to cairo functions,
// for `make fate-filter-drawvg-interpreter`.
// Comments.
lineargrad 0 0 1 1
colorstop 0 red // after a statement
colorstop
1 // in the middle of a statement
blue
// Constants.
setlinejoin miter
setlinecap round
// if/repeat
M 0 0
repeat 10 {
if (eq(i,3)) { break }
l (i) (i)
}
// User variables.
setvar foo -1
setvar bar -2
lineto foo (bar)
// Print
print 1 foo (bar - 2)
// State
save
restore
// Transformation matrix.
scale 1
scalexy 2 3
translate 4 5
rotate 6
resetmatrix
// Basic shapes
rect 1 2 3 4
circle 1 2 3
roundedrect 100 100 400 400 50
// Sources
setcolor #abcdef
stroke
lineargrad 0 1 2 3
colorstop 0 red 1 blue
stroke
radialgrad 1 2 3 4 5 6
repeat 2 { colorstop (i/10) black ((i+1)/10) white }
stroke
// Curves. The next line should be compatible with SVG's <path>.
M 10,50 Q 25,25 40,50 t 30,0 30,0 c 20 50 40 -50 100 0
// Preserve
preserve fill
preserve eofill
preserve stroke
preserve clip
// Fill/clip
fill eofill
clip eoclip
// Dashes
setdash 1 2 3
setdashoffset 4
resetdash
// Procedures
setvar a -1
setvar b -2
proc f2 a b { M a b break call invalid }
proc f1 a { call f2 a 2 }
proc f0 { call f1 1 }
call f0
l a b
// Colors
setrgba 0.1 0.2 0.3 0.4 fill
sethsla 100 0.2 0.3 0.4 fill
defrgba c0 0.5 0.6 0.7 0.8
defhsla c1 200 0.7 0.8 0.9
setcolor c0 fill
setcolor c1 fill
// Frame metadata
getmetadata md0 m.a
getmetadata md1 m.b
getmetadata md2 m.c
l md0 (md1 + 1) md2 0

View File

@@ -0,0 +1,10 @@
// Render a square, for `make fate-filter-drawvg-video`.
M 10 10
l 0 10
h 10
v -10
h -10
z
setcolor blue
stroke