Introduction
************

drawvg (draw vector graphics) is a language to draw two-dimensional graphics on top of video frames. It is not intended to be used as a general-purpose language. Since its scope is limited, it prioritizes being concise and easy to use.

For example, using the Canvas API (https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) we can render a triangle running this code in a Web browser:

     const canvas = document.getElementById("canvas");
     const ctx = canvas.getContext("2d");

     ctx.beginPath();
     ctx.moveTo(125, 50);
     ctx.lineTo(100, 100);
     ctx.lineTo(150, 100);
     ctx.closePath();
     ctx.stroke();

The same triangle can be written with this drawvg script:

     moveto 125 50
     lineto 100 100 150 100
     closepath
     stroke

It can be shortened using the aliases for moveto, lineto, and closepath:

     M 125 50
     L 100 100 150 100
     Z
     stroke

Both newlines (U+000A) and spaces (U+0020) can be used interchangeably as delimiters, so multiple commands can appear on the same line:

     M 125 50 L 100 100 150 100 Z
     stroke

Finally, drawvg can use FFmpeg expressions (ffmpeg-utils) and frame metadata in command arguments. In this example, we are using the variables W (frame width) and H (frame height) to create a circle in the middle of the frame.

     circle (w / 2) (h / 2) (w / 3)
     stroke

Many commands are a direct equivalent to a function in the Cairo graphics library (https://www.cairographics.org/). For such commands, the reference below provides a link to the related Cairo documentation.

Syntax
******

The syntax is heavily inspired by languages like Magick Vector Graphics (https://imagemagick.org/script/magick-vector-graphics.php), or SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path). Many command names are taken from PostScript (https://en.wikipedia.org/wiki/PostScript).

Structure
=========

A drawvg script consists of a series of commands to describe 2D graphics.

A command is an identifier (like setcolor or lineto) followed by its arguments. Each item in the code (command name, arguments, etc.) is separated by any of the following characters:

  * Space (' ')

  * Comma (',')

  * Newline ('\n')

  * Tabs ('\t')

  * Return ('\r')

The beginning of the item indicates how it will be interpreted:

//
     Comment

0, ..., 9, +, -
     Number literal

(
     Expression

{, }
     Block delimiters

Anything else
     Name of a command, a color, etc.

Comments
========

Comments start with two slashes (//), and stop at the end of the line (either a \n, or the end of the script).

     circle 100 100 50 // this is ignored
     fill

     // this is also ignored

// must appear after a space, or at the beginning of the line. If // is preceded by any non-blank character, the parser will consider // as part of the previous item.

For example, in this script:

     circle 10 10 50// something

The parser throws an error because it tries to parse 50// as a number literal.

Commands
========

The way commands are parsed is inspired by SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path):

  * Every command in the script starts with its name, and it is followed by zero or more arguments.

  * There are no explicit delimiters between commands or arguments.

     Most programming languages expect characters like parenthesis, commas, or semicolons, to separate items. For example:

          moveto(10, 10); lineto(20, 30);

     The equivalent in drawvg is:

          moveto 10 10 lineto 20 30

  * If the command has no arguments (like closepath or stroke), the next command starts at the next item.

In the next script there are 4 different commands:

     newpath rect 10 20 30 40 setcolor teal fill

 1. newpath requires no arguments.

 2. rect requires 4 arguments, so it takes the next 4 numbers.

 3. setcolor requires 1 argument, so it takes the word teal.

 4. fill requires no arguments.

Single-Letter Aliases
---------------------

Most commands in SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path) are also present in drawvg. For some of them, there is an alias to a longer name:

  * curveto for C.

  * rcurveto for c.

  * lineto for L.

  * rlineto for l.

  * moveto for M.

  * rmoveto for m.

  * closepath for Z, z.

Other commands only exist in a single-letter form:

  * H, h

  * Q, q

  * S, s

  * V, v

  * T, t

This makes it possible to use a path in SVG to create the same shape in a drawvg script.

Implicit Commands
-----------------

For many commands, the name can be omitted when it is used multiple times in successive calls.

In the reference below, these commands has a Can be Implicit note in their signature.

For example, in this script:

     M 50 50
     l 10 10
     l 10 -10
     l 10 10
     l 10 -10
     l 10 10
     stroke

After the first call to l (alias to rlineto), the command can be executed without the name, so it can be written as:

     M 50 50
     l 10 10 10 -10 10 10 10 -10 10 10
     stroke

To reuse the same command (l, in the previous example), the parser checks if the item after the last argument is a numeric value, like a number literal or a FFmpeg expression.

In this example:

     l 10 20 30 40 stroke

l requires 2 arguments, and can be implicit, so the parser performs this operation:

 1. Takes the two next items (10 and 20) and emits the first instruction.

 2. Checks if the item after 20 is a numeric value. Since it is 30, it takes 30 and 40 and emits the second instruction (l 30 40).

 3. Checks if the next item after 40 is a numeric value, but it is a command (stroke), so it stops reusing l.

This is another feature taken from SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path). An important difference with SVG is that the separator between items is always required. In SVG, it can be omitted in some cases. For example, the expression m1-2 is equivalent to m 1 -2 in SVG, but a syntax error in drawvg.

Arguments
=========

Most commands expect numeric arguments, like number literals, variable names, or expressions.

setcolor and colorstop expect a color.

setlinecap and setlinejoin expect a constant value.

Number Literals
---------------

A number literal is an item in the script that represents a constant value. Any item that starts with a decimal digit (between 0 and 9), a - or a +, is interpreted as a number literal.

The value is parsed with av_strtod (https://ffmpeg.org/doxygen/trunk/eval_8c.html#a7d21905c92ee5af0bb529d2daf8cb7c3). It supports the prefix 0x to write a value with hexadecimal digits, and many units (https://ffmpeg.org/ffmpeg-utils.html#:~:text=The%20evaluator%20also%20recognizes%20the%20International%20System%20unit%20prefixes) (like K or GiB).

In the next example, all literals represent the same value:

     10000
     1e4
     10K
     0x2710

Expressions
-----------

FFmpeg expressions (ffmpeg-utils) can be used as arguments for any command that expects a numeric argument. The expression must be enclosed in parenthesis.

The variables W and H represent the width and height of the frame. We can compute the center of the frame by dividing them by 2:

     M (w / 2) (h / 2)

They can also contain parenthesis (to group operations, to call functions, etc):

     moveto
         ((w + 10) / 2)      // x
         (h / (2 * cos(t)))  // y

The variables N and T can be used to compute a value that changes over time.

To draw a circle oscillating from left to right, we can use an expression based on sin(t) for the x coordinate:

     circle
         (w / 2 + sin(2 * t) * w / 4)  // x
         (h / 2)                       // y
         (w / 5)                       // radius

     stroke

Expressions can be split in multiple lines, but they can't contain comments within them.

     moveto   // This is a comment.
         (w   // This is part of the expression, not a comment.
          + h)

Variable Names
--------------

When an expression is only a reference to a variable, the parenthesis can be omitted, and the item is just the variable name.

The next 3 expressions are equivalent: in all cases, they create a rectangle covering the whole frame.

     rect (0) (0) (w) (h)

     rect 0 0 w h

     rect (0) 0 (w) h

It is possible to create a variable with the same name of a command, and then use it as an argument. In the previous example, the item H is a reference to a variable (frame height), but in other contexts it may be a command (h).

For implicit commands, the parser prioritizes commands over variable names when it has to determine if the command is reused.

In this example, the variable C is used as the first argument in two calls to l. However, only the first one is valid, because in the second call the parser recognizes c as a command.

     setvar c 5
     l c 10 c 15

This issue can be fixed by surrounding the start of the second call with parenthesis:

     setvar c 5
     l c 10 (c) 15

Colors
------

The color to stroke and to fill paths can be set with setcolor. Its argument has the same syntax for colors in FFmpeg:

  * A predefined color name (ffmpeg-utils).

  * In #RRGGBB format.

  * Optionally, an @a suffix can be added to set the alpha value, where a is a number between 0 and 1.

The color can be a variable name. In that case, its value is interpreted as a 0xRRGGBBAA code.

     circle 75 100 50
     setcolor #FF0000
     fill

     circle 125 100 50
     setvar CustomGreen 0x90EEAAFF
     setcolor CustomGreen
     fill

     circle 175 100 50
     setcolor blue@0.5
     fill

The commands setrgba and sethsla allow setting colors using expressions.

defrgba and defhsla compute the color and store it in a variable.

Constants
---------

The argument for setlinecap and setlinejoin is an identifier referring to a constant value.

     setlinecap round

Guide
*****

Paths
=====

A path is a complex shape, composed by lines and curves, that can be used to fill a region, to stroke an outline, or to establish a clip region.

In order to draw anything on top of a video frame, first we have to define a path, and then use stroke or fill.

The tutorial on paths in MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths) is a good introduction to the topic. It is focused on SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path), but the same concepts can be applied in drawvg.

Current Point
-------------

Some commands require a current point. Initially, the current point is set to NaN (https://en.wikipedia.org/wiki/NaN). It is initialized with M or moveto. Other commands, like lineto or curveto, updates the current point to the new end of the shape.

The current point can be cleared with newpath. Commands that clear the path, like stroke or fill, also clear the current point.

rlineto uses coordinates relative to the current point.

Given this script:

     moveto 20 100
     rlineto 150 -90
     rlineto -50 200
     closepath
     stroke

These are the coordinates of the current point after executing each command:

Command   Current Point
moveto 20 100   20, 100
rlineto 150 -90   170, 10
rlineto -10 50   140, 210
closepath   20, 100

The same script can be written with single-letter aliases:

     M 20 100 l 150 -90 -50 200 z stroke

Defining a Shape
----------------

A path is defined by adding lines, curves, or basic shapes.

  * Basic shapes

       * circle

       * ellipse

       * rect

       * roundedrect

  * Lines

       * M, moveto

       * m, rmoveto

       * H, h

       * V, v

       * L, lineto

       * l, rlineto

       * Z, z, closepath

  * Curves

       * arc, arcn

       * C, curveto,

       * c, rcurveto

       * Q, q

       * S, s

       * T, t

Single-letter commands are taken from SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path).

Fill
----

The region within the shape defined by a path can be filled with fill or eofill. Each command uses a different fill rule (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-fill-rule-t):

  * fill uses the winding rule (https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-WINDING:CAPS), also known as nonzero rule (https://en.wikipedia.org/wiki/Nonzero-rule).

  * eofill uses the even--odd rule (https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS).

This script shows the difference between the
winding (https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-WINDING:CAPS)
and
even--odd (https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS)
rules:

     rect 50 10 100 60
     circle 150 70 40
     setcolor seagreen
     fill

     rect 50 130 100 60
     circle 150 190 40
     setcolor skyblue
     eofill

Stroke
------

stroke draws a line around the shape defined by the path. The stroke can be configured with different commands:

  * setdash

  * setdashoffset

  * setlinecap

  * setlinejoin

  * setlinewidth

  * resetdash

This example use setdashoffset to animate the stroke:

     moveto 0 0
     lineto w h

     setlinecap round
     setdash 50 50
     setlinewidth 20
     setdashoffset (hypot(w, h) * t / -3)
     setcolor seagreen

     stroke

Clip
----

A clip region (https://en.wikipedia.org/wiki/Clipping_(computer_graphics)) can be established with clip and eoclip.

If there is an active clip region, the new clip region will be the intersection between the existing one and the path. resetclip reset the clip region to the whole frame.

eoclip uses the even--odd rule (https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS) to compute the clip region.

codeexample

     rect 50 50 100 200
     clip

     circle 30 30 150
     setcolor seagreen
     fill

     // Draw outside the clip region.
     resetclip
     circle 30 30 150
     setlinewidth 3
     setcolor skyblue
     stroke

Preserving Paths
----------------

The path is cleared after any operation on it, like fill or stroke. To reuse the same path in multiple operations, preserve must be called before them.

In this example, each path is used twice.

     circle 120 120 50
     setcolor seagreen
     preserve stroke
     clip

     circle 100 100 50
     setcolor skyblue
     preserve fill
     setcolor tomato
     stroke

Variables
=========

A drawvg can use some variables, provided by the interpreter, to compute values in FFmpeg expressions (ffmpeg-utils):

cx
     X coordinate of the current point.

cy
     Y coordinate of the current point.

w
     Width, in pixels, of the frame.

h
     Height, in pixels, of the frame.

i
     The loop counter in repeat blocks.

n
     Frame number.

t
     Timestamp, in seconds.

ts
     Timestamp, in seconds, of the first frame.

duration
     Duration, in seconds, of the frame.

User Variables
--------------

New variables can be created with the setvar command. It associates a name with a numeric value.

The name must follow these rules:

  * It must start with an ASCII letter or an underscore (_).

  * It can contain only ASCII letters, underscores, and digits.

  * It must not match the name of a variable provided by the interpreter (like W or T).

The same variable can be assigned multiple times.

In this example, the result of an expression is stored in a variable
with the name PROGRESS. Then, it is used for the X and
WIDTH arguments of rect.

     setvar progress (w * (pow(mod(t / 2 + 0.5, 1), 2.5)))

     rect ((w - progress) / 2) 0 progress h

     setcolor darkblue
     fill

Currently, a script can contain only 20 different variable names, but this limit can be modified in the future.

Patterns
========

The pattern for fill and stroke operations can be either a solid color, or a gradient.

  * Solid colors.

       * setcolor

       * sethsla

       * setrgba

  * Gradients.

       * lineargrad

       * radialgrad

The pattern is not cleared after being used in a fill or stroke operation, but it is replaced by any command that sets a new pattern.

Gradients
---------

To configure a gradient, first call to lineargrad or radialgrad, and then add color stops by calling colorstop for each stop.

In this example, the whole frame is filled with a linear gradient:

     lineargrad 0 0 w h
     colorstop 0 skyblue
     colorstop 1 darkblue

     rect 0 0 w h
     fill

In this example, a radial gradient is used to simulate a sphere:

     radialgrad 90 90 5 120 120 100
     colorstop 0.0 #90DDFF
     colorstop 0.9 #000030
     colorstop 1.0 #000000

     rect 0 0 w h
     fill

Variables
---------

setcolor and colorstop accept a variable name as the argument. When a variable is used, its value is interpreted as a 0xRRGGBBAA code.

codeexample

     // Use color #1020FF, alpha = 50%
     setvar someblue 0x1020FF7F

     setcolor someblue

     rect 30 30 120 120
     fill

     rect 90 90 120 120
     fill

If a variable has the same name of a known color (ffmpeg-utils), the variable has preference, and will be used instead of the predefined color.

codeexample

     setcolor teal
     rect 30 30 120 120
     fill

     setvar teal 0x70AAAAFF  // Now, `teal` is #70AAAA
     setcolor teal
     rect 90 90 120 120
     fill

defrgba and defhsla compute the 0xRRGGBBAA value for a color given its color components:

  * For defrgba: red, green, blue, and alpha.

  * For defhsla: hue, saturation, lightness, and alpha.

Each color component must be in range 0 to 1, except hue, which is 0 to 360.

codeexample

     defrgba colorA 1 0.5 0.25 1     // colorA = RGB(255, 127, 63)
     defhsla colorB 200 0.75 0.25 1  // colorB = HSL(200, 75%, 25%)

     rect 0 0 (w / 2) h
     setcolor colorA
     fill

     rect (w / 2) 0 (w / 2) h
     setcolor colorB
     fill

Transformations
===============

The coordinates for each command can be scaled, rotated, and translated, by using the following commands:

  * rotate

  * scale

  * scalexy

  * translate

The transformations are applied when the command is executed. They have no effect on the existing path, only on the new segments added to it.

They are done by updating the current transformation matrix (https://www.cairographics.org/manual/cairo-Transformations.html) in the Cairo context. To reset the matrix to its original state, before any transformation, use resetmatrix.

The transform origin for scale and rotation is initially at 0, 0, but it can be adjusted with translate.

codeexample

     // Map (0, 0) as the center of the frame.
     translate (w / 2) (h / 2)

     // Scale the space as if the frame is 1x1 pixel.
     scalexy w h

     // Draw multiple lines with the same arguments,
     // but each one on a different rotation.
     repeat 10 {
         rotate (PI / 10)
         M -0.25 0
         H 0.25
     }

     // Reset transformations, so the scale does not
     // affect stroke.
     resetmatrix

     stroke

State Stack
===========

The state of a drawvg script contains all parameters used for drawing operations, like the current color, the transformation matrix, the stroke configuration, etc.

The save command pushes a snapshot of the state to an internal stack. Later, restore pops the latest snapshot from the stack, and uses it as the new state.

The parameters that can be saved and restored are:

  * Pattern for stroke and fill operations.

       * lineargrad

       * radialgrad

       * setrgba

       * setcolor

       * sethsla

  * Transformation matrix.

       * resetmatrix

       * rotate

       * scale

       * scalexy

       * translate

  * Stroke configuration.

       * setdash

       * setdashoffset

       * setlinecap

       * setlinejoin

       * setlinewidth

  * Clip region

       * clip

       * resetclip

Frame Metadata
==============

Some FFmpeg filters add metadata to frames. The command getmetadata can read metadata items containing a numeric value, and store it in a variable that can be used for command arguments.

The cropdetect filter computes the parameters to remove empty
regions around the video. These parameters are accessible in the
lavfi.cropdetect keys of the frame metadata.

     // Get metadata from cropdetect filter and store it
     // in `cd*` variables.
     getmetadata cdx lavfi.cropdetect.x
     getmetadata cdy lavfi.cropdetect.y
     getmetadata cdw lavfi.cropdetect.w
     getmetadata cdh lavfi.cropdetect.h

     rect cdx cdy cdw cdh
     setcolor yellow@0.5
     setlinewidth 10
     stroke

To test the script, copy it to a drawcropdetect.vgs file, and then execute a command like this:

     ffplay -i example-video.webm -vf 'cropdetect, drawvg=file=drawcropdetect.vgs'

if / repeat Statements
======================

There is limited support for control flow statements: only if and repeat.

Both commands receive two arguments: an expression and a block.

     if (condition) {
         // commands
     }

     repeat (count) {
         // commands
     }

if executes its block if the result of (condition) is not zero.

repeat executes its block the number of times specified by (count). In each iteration, the variable I is used as a loop counter (https://en.wikipedia.org/wiki/For_loop#Loop_counters).

If the result of the expression is not a finite number (like NaN (https://en.wikipedia.org/wiki/NaN)) the block is not executed.

Comparison and Logical Operators
--------------------------------

FFmpeg expressions (ffmpeg-utils) only supports arithmetic operators (like + for addition). Comparison operators (like !=) are supported via functions, while logical operators (like && for AND) can be emulated with arithmetic operations.

Expression   FFmpeg Equivalent
x = y   eq(x, y)
x < y   lt(x, y)
x > y   gt(x, y)
x ≤ y   lte(x, y)
x ≥ y   gte(x, y)
a ≤ x ≤ b   between(x, a, b)

Logical operators can be emulated with multiplication (for AND), or addition (for OR):

Expression   FFmpeg Equivalent
x OR y   x + y
x AND y   x * y

In other programming languages, a code like this:

     if (x > y && z != 1) {
         // …
     }

Can be written for drawvg like this:

     if (gt(x, y) * not(eq(z, 1))) {
         // …
     }

Early Exit
----------

break causes a repeat loop to be terminated immediately.

If it is executed outside a repeat block, it terminates the whole script, or the current procedure.

In this example, we are using the randomg function
to draw a line with random segments.

The loop can be executed 500 times, but it is interrupted if the X
coordinate of the current point (CX) exceeds the frame width
(W). The current point is updated after each call to
rlineto.

     moveto 0 0

     repeat 500 {
         rlineto
             (randomg(0) * 15)
             (randomg(0) * 20)

         if (gt(cx, w)) {
             break
         }
     }

     stroke

Procedures
==========

A procedure is a name associated with a block that can be executed multiple times. It can take between 0 and 6 parameters.

proc is used to set the parameter names and the block for a procedure:

     proc p0 {
         // …
     }

     proc p1 param1 param2 {
         // …
     }

Inside the block, the arguments can be accessed as regular variables:

     proc square center_x center_y side {
         rect
             (center_x - side / 2) (center_y - side / 2)
             side side
     }

call executes the block assigned to the procedure name. It requires the name of the procedure, and the value for each parameter defined in the call to proc.

     call p0

     call p1 1 2

     call square (w / 2) (h / 2) (w / t)

In this example, the procedure zigzag draws multiple lines from
the current point.

     setvar len (w / 10)
     setlinewidth 5

     proc zigzag {
         repeat 10 {
             l len len len (-len)
         }

         stroke
     }

     setcolor #40C0FF
     M 0 60
     call zigzag

     setcolor #00AABB
     M 0 120
     call zigzag

     setcolor #20F0B7
     M 0 180
     call zigzag

The color and the Y coordinate of the starting point can be sent as procedure arguments:

     setvar len (w / 10)
     setlinewidth 5

     proc zigzag color y {
         setcolor color

         M 0 y
         repeat 10 {
             l len len len (-len)
         }

         stroke
     }

     call zigzag 0x40C0FFFF 60
     call zigzag 0x00AABBFF 120
     call zigzag 0x20F0B7FF 180

When the procedure returns, the value of the variable for each argument is restored to the value it had before calling the procedure. Changes in other variables (with setvar, getmetadata, defhsla, and defrgba) are preserved.

In the next example, the variable A has the value 0 before
calling the procedure P. During the execution of P,
A is 1, but after it, A is 0 again.

     setvar A 0

     proc P A {
         print A
     }

     print A
     call P 1
     print A

It writes the following messages:

[7:7] A = 0.000
[4:8] A = 1.000
[9:7] A = 0.000

break causes the script to leave the current procedure, similar to the return statement (https://en.wikipedia.org/wiki/Return_statement) in other programming languages, unless it is called within a repeat loop.

The body of the procedure must be defined with proc before using call.

In this example, when the procedure notyet is called, its body
has not yet defined, so the execution fails with the error
Missing body for procedure 'notyet'.

     call notyet

     proc notyet {
         // ...
     }

A procedure can be redefined by other calls to proc with the same name. In such case, call invokes the last assigned block.

In this example, the procedure example has two different blocks.

     proc example {
         // block1
     }

     call example    // executes block1

     proc example {
         // block2
     }

     call example    // executes block2

Functions in Expressions
========================

There are some functions specific to drawvg available in FFmpeg expressions (ffmpeg-utils).

Function p
----------

p(x, y) returns the color of the pixel at coordinates x, y, as a 0xRRGGBBAA value. This value can be assigned to a variable, which can be used later as the argument for setcolor.

If the coordinates are outside the frame, or any of the arguments is not a finite number (like NaN (https://en.wikipedia.org/wiki/NaN)), the function returns NaN.

The transformation matrix is applied to the arguments. To use the original frame coordinates, call resetmatrix between save and restore:

     save
     resetmatrix
     setvar pixel (p(0, 0))    // top-left pixel of the frame.
     restore

     setcolor pixel

Bitwise operations can be used to extract individual color components:

     setvar pixel (p(x, y))

     if (not(isnan(pixel))) {
         setvar px_red   (pixel / 0x1000000)
         setvar px_green (bitand(pixel / 0x10000, 0xFF))
         setvar px_blue  (bitand(pixel / 0x100, 0xFF))
         setvar px_alpha (bitand(pixel, 0xFF))
     }

Function pathlen
----------------

pathlen(n) computes the length of the current path, by adding the length of each line segment returned by cairo_copy_path_flat (https://www.cairographics.org/manual/cairo-Paths.html#cairo-copy-path-flat).

The function expects an argument N, as the maximum number of line segments to add to the length, or 0 to add all segments.

In this example, pathlen is used to animate the stroke of a
spiral, in a 5 seconds loop.

     M (w / 2) (h / 2)

     setvar a -1
     repeat 16 {
         rcurveto
             (a * 2 / 3) 0
             (a * 2 / 3) (a)
             0 (a)

         setvar a (-sgn(a) * (abs(a) + 10))
     }

     setlinewidth 3
     setdash
         (pathlen(0) * (1 - mod(t / 5, 1)))
         1e6

     setcolor teal
     stroke

Function randomg
----------------

randomg(idx) is similar to the random(idx) function, available in FFmpeg expressions (ffmpeg-utils), but its state is global to the frame, instead of specific to each expression.

To understand the difference, we need to dive into how random(idx) works inside a drawvg script.

First, each expression in FFmpeg has a set of 10 internal variables, which can be written with st(idx, value), and can be read with ld(idx). IDX is a value between 0 and 9. These variables are initialized to 0.

When a drawvg script is parsed, each expression is compiled with av_expr_parse (https://ffmpeg.org/doxygen/8.0/eval_8h.html#ad3bf8f3330d1fd139de2ca156c313f34), from libavutil (https://ffmpeg.org/libavutil.html), and these compiled expressions are reused for every frame. The changes in the internal variables (with st(idx, value)) are visible between frames, but they are not shared between expressions.

In this example, the expression for the X coordinate updates its
internal variable 0 in every frame:

     circle
         (st(0, mod(ld(0) + 15, w))) // X
         120                         // Y
         (ld(0) + 20)                // radius

     fill

st(idx, value) returns the updated value, so it can be used as the result of the expression.

The radius is not affected because its internal variable (from ld(0)) is not updated by the other expression.

Also, note that this example is just to show how internal variables are kept between frames. A better approach to create this animation is to use the variables n or t:

     circle (mod(n * 15, w)) 120 20
     fill

The function random(idx) returns a pseudorandom (https://en.wikipedia.org/wiki/Pseudorandom_number_generator) value between 0 and 1. IDX is the internal variable that is used both as the seed and to keep the state of the number generator.

The next example uses random(0) to generate a random value for
the center of a circle:

     circle
         (random(0) * w)
         (random(0) * h)
         10

     fill

The circle in every frame is at a different position, but always on the diagonal line of the frame. This happens because the values for the coordinates X and Y are identical, since both number generators use the same seed.

To distribute the circles over the whole frame we need different seeds for each expression. This can be achieved by writing a non-zero value (like 0xF0F0) to the internal variable of one of expressions, but only when its value is 0:

     circle
         (random(0) * w)
         (st(0, if(ld(0), ld(0), 0xF0F0)); random(0) * h)
         10

     fill

This approach is only useful if we need completely different positions in each frame. In the next example, random values are used to distribute many circles over the frame, but the position is fixed. The only change over time is the fill color:

     repeat 20 {
         circle
             (st(0, i + 1e5); random(0) * w)
             (st(0, i + 1e10); random(0) * h)
             10
     }

     sethsla (t * 60) 0.5 0.5 1
     preserve fill

     setcolor black@0.5
     setlinewidth 1
     stroke

This is achieved by using a precomputed state before calling random(0). The variable I, updated by repeat, is needed to compute different states in each iteration.

The randomg(idx) function, which is specific to drawvg scripts, is similar to random(idx), but intended to solve the previous problems:

  * All frames have the same seed.

  * The state is shared between expressions.

The parameter IDX has two uses:

  * The last two bits are the index of an internal state, so it is possible to have 4 different number generators.

  * The first call to randomg with a specific index will use the argument as the seed for the number generator in that index.

In a script like this:

     M (randomg(0xFF1)) (randomg(0xFF0))
     l (randomg(0xAA1)) (randomg(0xFF0))

There are 4 calls to randomg:

 1. The first call, with the argument 0xFF1, uses the internal state at index 1 (because 0xFF1 modulo 4 is 1).

     Since this is the first use of that index, the number generator is initialized with the seed 0xFF1.

 2. The second call has the same behaviour: it initializes the state at index 0 with the value 0xFF0.

 3. The third call has the argument 0xAA1, and it uses index 1. Since that state is already initialized (with the seed 0xFF1), the value 0xAA1 is ignored, and it returns the next number.

This example renders a simple rain animation, moving lines from top to
bottom.

randomg is used to distribute the lines over the frame, and to
apply different speeds to each one.

     rect 0 0 w h
     setcolor midnightblue
     fill

     setcolor white

     repeat 50 {
         setvar offset (t * (randomg(0) + 1))

         moveto
             (mod(randomg(0) + offset / 6, 1) * w)
             (mod(randomg(0) + offset, 1) * h)

         rlineto 6 36

         setlinewidth (randomg(1) / 2 + 0.2)
         stroke
     }

Tracing with print
==================

It is possible to trace the execution of a drawvg script by printing the value of an expression, either with the print command, or with the print function.

In both cases, the values are written to the FFmpeg log.

Printing expressions may have a noticeable impact on the performance, so it is preferable to use it only when necessary.

Function print
--------------

The function print(t) writes the value of t, and returns its argument.

Given a line line this:

     M (sin(2 * PI * t) * w) 0

We can see the values of sin(2 * PI * t) by surrounding it with a call to print():

     M (print(sin(2 * PI * t)) * w) 0

Executing this script with a 1 second / 8 FPS video shows the expected values for the sine function.

$ ffmpeg \
    -f lavfi \
    -i 'color=r=8:d=1, drawvg=M (print(sin(2 * PI * t)) * w) 0' \
    -f null /dev/null \
  |& grep 'Eval @'

[Eval @ 0x7f500f502d20] 0.000000
[Eval @ 0x7f4ff784b420] 0.707107
[Eval @ 0x7f4ff784ba20] 1.000000
[Eval @ 0x7f4ff784c020] 0.707107
[Eval @ 0x7f4ff784c620] 0.000000
[Eval @ 0x7f4ff784cc20] -0.707107
[Eval @ 0x7f4ff784d220] -1.000000
[Eval @ 0x7f4ff784d820] -0.707107

Command print
-------------

The command print accepts an arbitrary number of arguments, and for each one it writes:

  * The source location (line and column).

  * The source code of the expression.

  * The result of evaluating the expression.

When there are multiple expressions, they are separated by the | character.

The next script prints the position of the current point after the
l command:

     M 10 20
     l 100 100
     print cx cy
     stroke

For each frame, it produces this output:

[3:7] cx = 110.000000 | [3:10] cy = 120.000000

The next example prints the values of random(0):

$ ffmpeg \
    -f lavfi \
    -i 'color=r=8:d=1, drawvg=print (random(0))' \
    -f null /dev/null \
  |& grep 'drawvg @'

[drawvg @ 0x50a000000180] [1:7] (random(0)) = 0.229731
[drawvg @ 0x50a000000180] [1:7] (random(0)) = 0.959813
[drawvg @ 0x50a000000180] [1:7] (random(0)) = 0.071676
[drawvg @ 0x50a000000180] [1:7] (random(0)) = 0.044600
[drawvg @ 0x50a000000180] [1:7] (random(0)) = 0.134127
[drawvg @ 0x50a000000180] [1:7] (random(0)) = 0.320513
[drawvg @ 0x50a000000180] [1:7] (random(0)) = 0.857675
[drawvg @ 0x50a000000180] [1:7] (random(0)) = 0.562456

Commands
********

arc
===

arc XC YC RADIUS ANGLE1 ANGLE2    --- Can be implicit

Adds a circular arc of the given RADIUS to the current path. The arc is centered at XC, YC, begins at ANGLE1 and proceeds in the direction of increasing angles to end at ANGLE2.

If there is a current point, a line is added from it to the beginning of the arc. If this is not desired, use newpath before arc to clear the current point.

See the documentation of the cairo_arc (https://www.cairographics.org/manual/cairo-Paths.html#cairo-arc) function for more details.

codeexample

     arc 120 120 60 0 (3 * PI / 2)
     stroke

arcn
====

arcn XC YC RADIUS ANGLE1 ANGLE2    --- Can be implicit

Similar to arc, but it differs in the direction of the arc between the two angles.

See the documentation of the cairo_arc_negative (https://www.cairographics.org/manual/cairo-Paths.html#cairo-arc-negative) function for more details.

In this example, both arc and arcn have the same angles,
but they render different arcs:

     arc  120  90 60 (PI / 2) 0

     newpath
     arcn 120 150 60 (PI / 2) 0

     stroke

newpath is needed to prevent a line between the two arcs.

break
=====

break

break terminates the execution of the innermost block, either a repeat loop or a procedure.

If it is used outside of a repeat / proc block, it terminates the script for the current frame.

call
====

call NAME ARGS*

Invokes a procedure defined by proc.

See the Procedures section above for more details.

circle
======

circle XC YC RADIUS    --- Can be implicit

Adds a circle of the given RADIUS to the current path. The circle is centered at XC, YC. The current point is cleared before and after adding the circle.

This is a convenience wrapper for arc. A call to circle is equivalent to:

     newpath
     arc xc yc radius (0) (2 * PI)
     newpath

clip, eoclip
============

clip, eoclip

Establishes a new clip region by intersecting the current clip region with the current path as it would be filled by fill or eofill.

eoclip uses the even--odd rule (https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS). See fill rules for more details.

The path is cleared after updating the clip region, unless the preserve command is used before clip or eoclip.

See the documentation of the cairo_clip (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-clip) function for more details.

Z, z, closepath
===============

Z, z, closepath

Adds a line segment to the path from the current point to the beginning of the current sub-path, and closes this sub-path. The beginning is set by any of the move commands (M, m, moveto, rmoveto).

See the documentation of the cairo_close_path (https://www.cairographics.org/manual/cairo-Paths.html#cairo-close-path) function for more details.

colorstop
=========

colorstop OFFSET COLOR    --- Can be implicit

Adds a color stop to a gradient pattern.

OFFSET is a value between 0 and 1, and it specifies the location along the gradient's control vector.

This command must be executed after lineargrad or radialgrad.

Color stops can be added in any number of calls to colorstop. In the next example, the 3 blocks define the same gradient:

     // 1
     colorstop 0.0 red
     colorstop 0.5 green
     colorstop 1.0 blue

     // 2
     colorstop 0 red 0.5 green
     colorstop 1 blue

     // 3
     colorstop 0 red 0.5 green 1 blue

See the documentation of the cairo_pattern_add_color_stop_rgba (https://www.cairographics.org/manual/cairo-cairo-pattern-t.html#cairo-pattern-add-color-stop-rgba) function for more details.

In this example, color stops are added in a repeat loop.

     lineargrad 0 0 w h

     repeat 6 {
         defhsla s (i * 60) 0.8 0.5 1
         colorstop (i / 5) s
     }

     rect 0 0 w h
     fill

It is possible to avoid transitions between color stops by repeating the same color in two stops:

     lineargrad 0 0 w h

     repeat 6 {
         defhsla s (i * 60) 0.8 0.5 1
         colorstop (i / 5) s
         colorstop ((i + 1) / 5) s
     }

     rect 0 0 w h
     fill

C, curveto
==========

C, curveto X1 Y1 X2 Y2 X Y    --- Can be implicit

Draw a cubic Bézier curve from the current point to the end point specified by X, Y. The start control point is specified by X1, Y1 and the end control point is specified by X2, Y2.

The behaviour is identical to the C command in SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path). For more details, see Cubic Bézier Curve on MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/d#cubic_b%C3%A9zier_curve), and the Curve Commands section of the Paths tutorial on MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths#curve_commands).

codeexample

     moveto 20 20

     curveto
         0 (h / 2)           // start control point
         w (h / 2)           // end control point
         (w - 20) (h - 20)   // end point

     stroke

c, rcurveto
===========

c, rcurveto DX1 DY1 DX2 DY2 DX DY    --- Can be implicit

Like curveto, but the coordinates are relative to the current point.

defhsla
=======

defhsla varname H S L A

Similar to sethsla, but instead of establishing the color for stroke and fill operations, the computed color is stored as a 0xRRGGBBAA value in the variable VARNAME.

VARNAME can then be used as a color for setcolor and colorstop.

See sethsla for more details on how the color is computed.

defrgba
=======

defrgba varname R G B A

Computes a color from the red, green, blue, and alpha components, and assigns it to the variable VARNAME as a 0xRRGGBBAA value.

All components are values between 0 and 1. Values outside that range are clamped to it.

ellipse
=======

ellipse CX CY RX RY    --- Can be implicit

Adds an ellipse to the current path. Similar to circle, but it is possible to use different radius for both axes.

codeexample
ellipse 120 120 75 50
stroke

fill, eofill
============

fill, eofill

Fill the current path, using the current pattern (either a solid color or a gradient).

eofill uses the even--odd rule (https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS). See fill rules for more details.

The path is cleared after the operation, unless the preserve command is used before fill or eofill.

See the documentation of the cairo_fill (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-fill) function for more details.

getmetadata
===========

getmetadata VARNAME KEY

Get the value of a metadata entry created by another filter, and assign it to the variable VARNAME.

If there is no metadata entry for KEY, or its value is not a number, VARNAME is set to NaN (https://en.wikipedia.org/wiki/NaN).

See the Frame Metadata section above for an example.

H, h
====

H, h X    --- Can be implicit

Draw a horizontal line from the current point to x.

The coordinate for H is absolute, and for h it is relative to the current point.

if
==

if CONDITION { BLOCK }

Executes a block if the value of CONDITION is not zero, and a finite number (unlike NaN (https://en.wikipedia.org/wiki/NaN)).

See the Comparison and Logical Operators section above for more details on how to write conditional expressions.

lineargrad
==========

lineargrad X0 Y0 X1 Y1

Set the current pattern to a new linear gradient, along the line from the coordinates X0, Y0 to X1, Y1.

This gradient can be used for stroke and fill operations.

Use colorstop to set the color for each position in the gradient.

L, lineto
=========

L, lineto X Y    --- Can be implicit

Draw a line from the current point to the coordinates at X, Y.

See the documentation of the cairo_line_to (https://www.cairographics.org/manual/cairo-Paths.html#cairo-line-to) function for more details.

l, rlineto
==========

l, rlineto DX DY    --- Can be implicit

Like lineto, but the coordinates are relative to the current point.

M, moveto
=========

M, moveto X Y    --- Can be implicit

Begin a new sub-path, and set the current point to X, Y.

m, rmoveto
==========

m, rmoveto DX DY    --- Can be implicit

Like moveto, but the coordinates are relative to the current point.

newpath
=======

newpath

Begin a new sub-path. Like moveto, but there is no current point after it.

In the next example, newpath is used in the path on the right to
prevent the line connecting both arcs.

setlinewidth 3

setcolor skyblue
arcn 70 90 20 0 (PI)
arc 70 150 20 0 (PI)
stroke

setcolor seagreen
arcn 170 90 20 0 (PI)
newpath
arc 170 150 20 0 (PI)
stroke

preserve
========

preserve

Indicates that the next operation to fill, stroke, or clip, must preserve the path, so the same path can be used in multiple operations.

It has effect on these commands:

  * clip

  * eoclip

  * eofill

  * fill

  * stroke

The script can contain any command between preserve and the associated operation. This allows modifying other properties, like the current color.

In this example, the same path is used for both fill and
stroke, but with different colors.

circle (w / 2) (h / 2) (w / 3)

setcolor skyblue
preserve fill

setlinewidth 10
setcolor seagreen
stroke

preserve can be called multiple times, if the same path has to be used in 3 or more operations.

In this example, the path created by circle is used by
fill, stroke, and clip. After clip, the
path is cleared.

circle 100 100 50

preserve fill
preserve stroke
clip

print
=====

print EXPR    --- Can be implicit

Print its arguments to the FFmpeg log.

See the Command print section above for more details.

proc
====

proc NAME PARAMS* { BLOCK }

Assign the block and the parameters for the procedure NAME. The procedure can be called multiple times with the call command.

See the Procedures section above for more details.

Q
=

Q x1 y1 X Y

Draw a quadratic Bézier curve from the current point to the end point specified by X, Y. The control point is specified by X1, Y1.

The behaviour is identical to the Q command in SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path). For more details, see Quadratic Bézier curve on MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/d#quadratic_b%C3%A9zier_curve), and the Curve Commands section of the Paths tutorial on MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths#curve_commands).

codeexample
moveto 20 20

Q
    0 h                 // control point
    (w - 20) (h - 20)   // end point

stroke

q
=

q DX1 DY1 DX DY

Like Q, but the coordinates are relative to the current point.

radialgrad
==========

radialgrad CX0 CY0 RADIUS0 CX1 CY1 RADIUS1

Creates a new radial gradient between the two circles defined by CX0 CY0 RADIUS0 and CX1 CY1 RADIUS1. Each set of arguments is the coordinates of the center and the radius.

This gradient can be used for stroke and fill operations.

Use colorstop to set the color for each position in the gradient.

The animation in the next example shows how the two circles defined in
the radialgrad arguments interact with each other.

The red circle represent the circle for the CX0 CY0 RADIUS0
arguments, and the yellow circle is the one for the
CX1 CY1 RADIUS1 arguments.

setvar cx0 (mod(t * 30, w))
setvar cy0 120
setvar radius0 20

setvar cx1 120
setvar cy1 120
setvar radius1 70

radialgrad
    cx0 cy0 radius0
    cx1 cy1 radius1

colorstop
    0 lightblue
    1 darkblue

// Fill the frame with the gradient.
rect 0 0 w h
fill

// Draw inner circle.
circle cx0 cy0 radius0
setcolor red
stroke

// Draw outer circle.
circle cx1 cy1 radius1
setcolor yellow
stroke

rect
====

rect X Y WIDTH HEIGHT

Adds a rectangle of the given size (WIDTH × HEIGHT), at position X, Y, to the current path. The current point is cleared before and after adding the rectangle.

See the documentation of the cairo_rectangle (https://www.cairographics.org/manual/cairo-Paths.html#cairo-rectangle) function for more details.

repeat
======

repeat COUNT { BLOCK }

Executes a block the number of times indicated by COUNT.

In each iteration, the variable I is used as a loop counter (https://en.wikipedia.org/wiki/For_loop#Loop_counters). It takes the values from 0 to count - 1. When the loop is terminated, the variable is restored to the value before starting the loop.

If COUNT is less than 1, or it is not a finite number (like NaN (https://en.wikipedia.org/wiki/NaN)), the block is not executed.

resetclip
=========

resetclip

Reset the current clip region to its original state, covering the whole frame.

See the documentation of the cairo_reset_clip (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-reset-clip) function for more details.

resetdash
=========

resetdash

Disable the dash pattern to be used by stroke. This reverts any change made by setdash and setdashoffset.

It calls cairo_set_dash (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash) with num_dashes set to 0.

resetmatrix
===========

resetmatrix

Resets the current transformation matrix.

restore
=======

restore

Restores the state saved by a preceding call to save.

For more details, see the State Stack section above, and the cairo_restore (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-restore) function.

rotate
======

rotate ANGLE

Modifies the current transformation matrix by rotating the user-space axes by ANGLE radians.

See the documentation of the cairo_rotate (https://www.cairographics.org/manual/cairo-Transformations.html#cairo-rotate) function for more details.

In this example:

  * scalexy maps the coordinates to a 1x1 frame.

  * translate put 0, 0 at the center of the frame.

  * rotate rotates 45°.

  * resetmatrix reverts the transformations before stroke, so the line width is not affected by the scale.

scalexy w h
translate 0.5 0.5
rotate (PI / 4)
rect -0.25 -0.25 0.5 0.5
resetmatrix
stroke

roundedrect
===========

roundedrect X Y WIDTH HEIGHT RADIUS    --- Can be implicit

Like rect, but a circular arc is used for the corners.

The next example shows the same rectangle, with different values for the
corner radius.

The radius is computed by multiplying I (the
loop counter (https://en.wikipedia.org/wiki/For_loop#Loop_counters)) by
4.5. This number is chosen to make the last shape a perfect circle.

     repeat 9 {
         roundedrect
             (mod(i, 3) * 80 + 5)     // x
             (floor(i / 3) * 80 + 5)  // y
             70 70                    // size
             (i * 4.5)                // radius
     }

     stroke

save
====

save

Saves a copy of the current state on an internal stack. This copy can be restored later with restore.

For more details, see the State Stack section above, and the cairo_save (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-save) function.

scale
=====

scale SXY

Similar to scalexy, but the same value is used for both axes. It is equivalent to:

scalexy SXY SXY

scalexy
=======

scalexy SX SY

Modifies the current transformation matrix by scaling the X and Y user-space axes by SX and SY respectively.

See the documentation of the cairo_scale (https://www.cairographics.org/manual/cairo-Transformations.html#cairo-scale) function for more details.

See rotate for an example on combining multiple transformations.

setcolor
========

setcolor COLOR

Set a solid color as the current pattern for stroke and fill operations

See the Colors section above for more details.

setdash
=======

setdash LENGTH    --- Can be implicit

Sets the dash pattern to be used by stroke.

Each call to setdash adds a length to the pattern, alternating between on and off portions of the stroke.

After a call to setdash, resetdash is needed either to create a new pattern, or to discard the current one.

See the documentation of the cairo_set_dash (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash) function for more details.

setdashoffset
=============

setdashoffset OFFSET

Set the offset into the dash pattern at which the stroke should start.

setdash must be called before setdashoffset.

See the documentation of the cairo_set_dash (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash) function for more details.

The next animation shows the effect of setdashoffset when its
argument changes over time.

scalexy w h
M 0.5 1
curveto 0 0.5, 1 0.5, 0.5 0
resetmatrix

setdash 20 5 // 20 on, 5 off
setdashoffset (t * 100)

setlinewidth 20
stroke

sethsla
=======

sethsla H S L A

Set the current pattern to a solid color, given the hue, saturation, and lightness, and alpha components.

h is the hue, a value between 0 and 359. Negative values are clamped to 0, and values greater than 359 are interpreted as modulo 360.

s (saturation), l (lightness), and a (alpha), are values between 0 and 1.

The conversion to RGB is implemented according to the formulae from Wikipedia (https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB).

setlinecap
==========

setlinecap CAP

Set the current line cap style, which determines the shape used to draw the end points of lines.

CAP must be one of the following names:

  * butt

  * round

  * square

It calls to cairo_set_line_cap (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-cap) to set the line cap style.

This example draws 3 lines with the same length, each one with a
different line cap style:

setlinewidth 40

setlinecap butt
setcolor tomato
M 60 40 v 100 stroke

setlinecap round
setcolor seagreen
M 120 40 v 100 stroke

setlinecap square
setcolor skyblue
M 180 40 v 100 stroke

M 20 40 H 220 m 0 100 H 20
setcolor black@0.5
setlinewidth 2
stroke

setlinejoin
===========

setlinejoin JOIN

Sets the current line join style, which determines the shape used to join two line segments.

JOIN must be one of the following names:

  * bevel

  * miter

  * round

It calls to cairo_set_line_join (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-join) to set the line join style.

This example draws 3 lines with the same length, each one with a
different line join style:

setlinewidth 30

setlinejoin bevel
setcolor tomato
M 70 20 l 50 50 50 -50 stroke

setlinejoin miter
setcolor seagreen
M 70 90 l 50 50 50 -50 stroke

setlinejoin round
setcolor skyblue
M 70 160 l 50 50 50 -50 stroke

setlinewidth
============

setlinewidth WIDTH

Set the line width for stroke.

WIDTH is affected by the transformation matrix.

To specify a width that is not affected by other transformations, resetmatrix can be used between save / restore:

save

resetmatrix
setlinewidth 1
stroke

// Restore matrix after stroke.
restore

See the documentation of the cairo_set_line_width (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-width) function for more details.

setrgba
=======

setrgba R G B A

Set the current pattern to a solid color, given the red, green, blue, and alpha components.

All components are values between 0 and 1. Values outside that range are clamped to it.

setvar
======

setvar VARNAME VALUE

Set the variable VARNAME to VALUE.

See the User Variables section above for more details.

stroke
======

stroke

Strokes the current path according to the current line width, line join, line cap, and dash settings.

The path is cleared after the operation, unless the preserve command is used before stroke.

See the documentation of the cairo_stroke (https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-stroke) function for more details.

S, s
====

S, s X2 Y2 X Y    --- Can be implicit

Draw a smooth cubic Bézier curve from the current point to the end point specified by X, Y. The end control point is specified by X2, Y2.

The start control point is the reflection of the end control point of the previous curve command about the current point.

The behaviour is identical to the S command in SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path). For more details, see Cubic Bézier Curve on MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/d#cubic_b%C3%A9zier_curve), and the Curve Commands section of the Paths tutorial on MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths#curve_commands).

s is like S, but the coordinates are relative to the current point.

codeexample

     M 20 120

     c 25 -50, 25 50, 50 0

     repeat 3 {
         s 20 50, 50 0
     }

     stroke

translate
=========

translate TX TY

Modifies the current transformation matrix by translating the user-space origin by TX, TY.

See the documentation of the cairo_translate (https://www.cairographics.org/manual/cairo-Transformations.html#cairo-translate) function for more details.

T, t
====

T, t X Y    --- Can be implicit

Draw a smooth quadratic Bézier curve from the current point to the end point specified by X, Y.

The control point is the reflection of the control point of the previous curve command about the current point.

The behaviour is identical to the T command in SVG's <path> (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path). For more details, see Quadratic Bézier curve on MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/d#quadratic_b%C3%A9zier_curve), and the Curve Commands section of the Paths tutorial on MDN (https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths#curve_commands).

t is like T, but the coordinates are relative to the current point.

codeexample

     M 20 120

     q 10 -20, 20 0

     repeat 9 {
         t 20 0
     }

     stroke

V, v
====

V, v Y    --- Can be implicit

Draw a vertical line from the current point to y.

The coordinate for V is absolute, and for v it is relative to the current point.