| @documentencoding UTF-8 |
| |
| @settitle drawvg - Language Reference |
| @titlepage |
| @center @titlefont{drawvg - Language Reference} |
| @end titlepage |
| |
| @top |
| |
| @contents |
| |
| @macro codeexample {block} |
| @cartouche Example |
| \block\ |
| @end cartouche |
| @end macro |
| |
| @macro vgscmd {name} |
| @ref{cmd_\name\,,@code{\name\}} |
| @end macro |
| |
| @chapter Introduction |
| |
| drawvg (@emph{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 |
| @uref{https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API,Canvas |
| API} we can render a triangle running this code in a Web browser: |
| |
| @example |
| 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(); |
| @end example |
| |
| The same triangle can be written with this drawvg script: |
| |
| @example |
| moveto 125 50 |
| lineto 100 100 150 100 |
| closepath |
| stroke |
| @end example |
| |
| It can be shortened using the aliases for @vgscmd{moveto}, @vgscmd{lineto}, |
| and @vgscmd{closepath}: |
| |
| @example |
| M 125 50 |
| L 100 100 150 100 |
| Z |
| stroke |
| @end example |
| |
| Both newlines (@code{U+000A}) and spaces (@code{U+0020}) can be used |
| interchangeably as delimiters, so multiple commands can appear on the |
| same line: |
| |
| @example |
| M 125 50 L 100 100 150 100 Z |
| stroke |
| @end example |
| |
| @macro ffexprs |
| @ref{Expression Evaluation,,FFmpeg expressions,ffmpeg-utils} |
| @end macro |
| |
| Finally, drawvg can use @ffexprs{} and frame metadata in command arguments. In |
| this example, we are using the variables @var{w} (frame width) and @var{h} |
| (frame height) to create a circle in the middle of the frame. |
| |
| @example |
| circle (w / 2) (h / 2) (w / 3) |
| stroke |
| @end example |
| |
| Many commands are a direct equivalent to a function in the |
| @uref{https://www.cairographics.org/,Cairo graphics library}. For such |
| commands, the reference below provides a link to the related Cairo |
| documentation. |
| |
| @chapter Syntax |
| |
| @macro svgpathlink |
| @uref{https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/path,SVG's @code{<path>}} |
| @end macro |
| |
| The syntax is heavily inspired by languages like |
| @uref{https://imagemagick.org/script/magick-vector-graphics.php,Magick |
| Vector Graphics}, or @svgpathlink{}. Many command names are taken from |
| @uref{https://en.wikipedia.org/wiki/PostScript,PostScript}. |
| |
| @section Structure |
| |
| A drawvg script consists of a series of commands to describe 2D |
| graphics. |
| |
| A command is an identifier (like @vgscmd{setcolor} or @vgscmd{lineto}) |
| followed by its arguments. Each item in the code (command name, |
| arguments, etc.) is separated by any of the following characters: |
| |
| @itemize |
| @item Space (@code{' '}) |
| @item Comma (@code{','}) |
| @item Newline (@code{'\n'}) |
| @item Tabs (@code{'\t'}) |
| @item Return (@code{'\r'}) |
| @end itemize |
| |
| The beginning of the item indicates how it will be interpreted: |
| |
| @table @r |
| @item @code{//} |
| Comment |
| @item @code{0}, @dots{}, @code{9}, @code{+}, @code{-} |
| Number literal |
| @item @code{(} |
| Expression |
| @item @code{@{}, @code{@}} |
| Block delimiters |
| @item Anything else |
| Name of a command, a color, etc. |
| @end table |
| |
| @section Comments |
| |
| Comments start with two slashes (@code{//}), and stop at the end of the |
| line (either a @code{\n}, or the end of the script). |
| |
| @example |
| circle 100 100 50 // this is ignored |
| fill |
| |
| // this is also ignored |
| @end example |
| |
| @code{//} must appear after a space, or at the beginning of the line. If |
| @code{//} is preceded by any non-blank character, the parser will |
| consider @code{//} as part of the previous item. |
| |
| For example, in this script: |
| |
| @example |
| circle 10 10 50// something |
| @end example |
| |
| The parser throws an error because it tries to parse @code{50//} as a |
| number literal. |
| |
| @section Commands |
| |
| The way commands are parsed is inspired by @svgpathlink{}: |
| |
| @itemize |
| @item |
| Every command in the script starts with its name, and it is followed by |
| zero or more arguments. |
| |
| @item |
| There are no explicit delimiters between commands or arguments. |
| |
| Most programming languages expect characters like parenthesis, commas, |
| or semicolons, to separate items. For example: |
| |
| @example |
| moveto(10, 10); lineto(20, 30); |
| @end example |
| |
| The equivalent in drawvg is: |
| |
| @example |
| moveto 10 10 lineto 20 30 |
| @end example |
| |
| @item |
| If the command has no arguments (like @vgscmd{closepath} or |
| @vgscmd{stroke}), the next command starts at the next item. |
| |
| @end itemize |
| |
| @codeexample{ |
| In the next script there are 4 different commands: |
| |
| @example |
| newpath rect 10 20 30 40 setcolor teal fill |
| @end example |
| |
| @enumerate |
| @item |
| @vgscmd{newpath} requires no arguments. |
| |
| @item |
| @vgscmd{rect} requires 4 arguments, so it takes the next 4 numbers. |
| |
| @item |
| @vgscmd{setcolor} requires 1 argument, so it takes the word @code{teal}. |
| |
| @item |
| @vgscmd{fill} requires no arguments. |
| @end enumerate |
| } |
| |
| @subsection Single-Letter Aliases |
| |
| Most commands in @svgpathlink{} are also present in drawvg. For some of them, |
| there is an alias to a longer name: |
| |
| @itemize |
| @item @vgscmd{curveto} for @vgscmd{C}. |
| @item @vgscmd{rcurveto} for @vgscmd{c}. |
| @item @vgscmd{lineto} for @vgscmd{L}. |
| @item @vgscmd{rlineto} for @vgscmd{l}. |
| @item @vgscmd{moveto} for @vgscmd{M}. |
| @item @vgscmd{rmoveto} for @vgscmd{m}. |
| @item @vgscmd{closepath} for @vgscmd{Z}, @vgscmd{z}. |
| @end itemize |
| |
| Other commands only exist in a single-letter form: |
| |
| @itemize |
| @item @vgscmd{H}, @vgscmd{h} |
| @item @vgscmd{Q}, @vgscmd{q} |
| @item @vgscmd{S}, @vgscmd{s} |
| @item @vgscmd{V}, @vgscmd{v} |
| @item @vgscmd{T}, @vgscmd{t} |
| @end itemize |
| |
| This makes it possible to use a path in SVG to create the same shape in |
| a drawvg script. |
| |
| @anchor{implicit commands} |
| @subsection 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 @emph{Can be Implicit} note |
| in their signature. |
| |
| @codeexample { |
| For example, in this script: |
| |
| @example |
| M 50 50 |
| l 10 10 |
| l 10 -10 |
| l 10 10 |
| l 10 -10 |
| l 10 10 |
| stroke |
| @end example |
| |
| After the first call to @vgscmd{l} (alias to @vgscmd{rlineto}), the command |
| can be executed without the name, so it can be written as: |
| |
| @example |
| M 50 50 |
| l 10 10 10 -10 10 10 10 -10 10 10 |
| stroke |
| @end example |
| } |
| |
| To reuse the same command (@vgscmd{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. |
| |
| @codeexample{ |
| In this example: |
| |
| @example |
| l 10 20 30 40 stroke |
| @end example |
| |
| @vgscmd{l} requires 2 arguments, and can be implicit, so the parser |
| performs this operation: |
| |
| @enumerate |
| |
| @item |
| Takes the two next items (@code{10} and @code{20}) and emits the first |
| instruction. |
| |
| @item |
| Checks if the item after @code{20} is a numeric value. Since it is |
| @code{30}, it takes @code{30} and @code{40} and emits the second |
| instruction (@code{l 30 40}). |
| |
| @item |
| Checks if the next item after @code{40} is a numeric value, but it is a |
| command (@vgscmd{stroke}), so it stops reusing @vgscmd{l}. |
| |
| @end enumerate |
| } |
| |
| This is another feature taken from @svgpathlink{}. 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 @code{m1-2} is equivalent to |
| @code{m 1 -2} in SVG, but a syntax error in drawvg. |
| |
| @section Arguments |
| |
| Most commands expect numeric arguments, like number literals, variable |
| names, or expressions. |
| |
| @vgscmd{setcolor} and @vgscmd{colorstop} expect a color. |
| |
| @vgscmd{setlinecap} and @vgscmd{setlinejoin} expect a constant value. |
| |
| @subsection 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 @code{0} and |
| @code{9}), a @code{-} or a @code{+}, is interpreted as a number literal. |
| |
| The value is parsed with |
| @uref{https://ffmpeg.org/doxygen/trunk/eval_8c.html#a7d21905c92ee5af0bb529d2daf8cb7c3,@code{av_strtod}}. |
| It supports the prefix @code{0x} to write a value with hexadecimal |
| digits, and |
| @uref{https://ffmpeg.org/ffmpeg-utils.html#:~:text=The%20evaluator%20also%20recognizes%20the%20International%20System%20unit%20prefixes,many |
| units} (like @code{K} or @code{GiB}). |
| |
| In the next example, all literals represent the same value: |
| |
| @example |
| 10000 |
| 1e4 |
| 10K |
| 0x2710 |
| @end example |
| |
| @subsection Expressions |
| |
| @ffexprs{} can be used as arguments for any command that expects a numeric |
| argument. The expression must be enclosed in parenthesis. |
| |
| @codeexample { |
| The variables @var{w} and @var{h} represent the width and height of the |
| frame. We can compute the center of the frame by dividing them by @code{2}: |
| |
| @example |
| M (w / 2) (h / 2) |
| @end example |
| |
| They can also contain parenthesis (to group operations, to call functions, |
| etc): |
| |
| @example |
| moveto |
| ((w + 10) / 2) // x |
| (h / (2 * cos(t))) // y |
| @end example |
| } |
| |
| The variables @var{n} and @var{t} can be used to compute a value that changes |
| over time. |
| |
| @codeexample { |
| To draw a circle oscillating from left to right, we can use an |
| expression based on @code{sin(t)} for the @code{x} coordinate: |
| |
| @example |
| circle |
| (w / 2 + sin(2 * t) * w / 4) // x |
| (h / 2) // y |
| (w / 5) // radius |
| |
| stroke |
| @end example |
| } |
| |
| Expressions can be split in multiple lines, but they can't contain |
| comments within them. |
| |
| @example |
| moveto // This is a comment. |
| (w // This is part of the expression, not a comment. |
| + h) |
| @end example |
| |
| @subsection 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. |
| |
| @codeexample { |
| The next 3 expressions are equivalent: in all cases, they create a |
| rectangle covering the whole frame. |
| |
| @example |
| rect (0) (0) (w) (h) |
| |
| rect 0 0 w h |
| |
| rect (0) 0 (w) h |
| @end example |
| } |
| |
| 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 @var{h} is a |
| reference to a variable (frame height), but in other contexts it may be |
| a command (@vgscmd{h}). |
| |
| For @ref{implicit commands}, the parser prioritizes |
| commands over variable names when it has to determine if the command is |
| reused. |
| |
| @codeexample { |
| In this example, the variable @var{c} is used as the first argument in two |
| calls to @vgscmd{l}. However, only the first one is valid, because in the |
| second call the parser recognizes @vgscmd{c} as a command. |
| |
| @example |
| setvar c 5 |
| l c 10 c 15 |
| @end example |
| |
| This issue can be fixed by surrounding the start of the second call with |
| parenthesis: |
| |
| @example |
| setvar c 5 |
| l c 10 (c) 15 |
| @end example |
| } |
| |
| @anchor{Colors} |
| @subsection Colors |
| |
| The color to stroke and to fill paths can be set with @vgscmd{setcolor}. |
| Its argument has the same syntax for colors in FFmpeg: |
| |
| @itemize |
| @item |
| A @ref{Color,,predefined color name,ffmpeg-utils}. |
| |
| @item |
| In @code{#RRGGBB} format. |
| |
| @item |
| Optionally, an @code{@@a} suffix can be added to set the alpha value, |
| where @code{a} is a number between @code{0} and @code{1}. |
| @end itemize |
| |
| @example |
| circle 70 70 60 |
| setcolor #FF0000 |
| fill |
| |
| circle 170 170 60 |
| setcolor blue@@0.5 |
| fill |
| @end example |
| |
| The color can be a variable name. In that case, it must be assigned with |
| @vgscmd{defrgba}, @vgscmd{defhsla}, or @vgscmd{setvar} and a color. |
| |
| @example |
| circle 70 70 60 |
| setvar CustomGreen #22FF44 |
| setcolor CustomGreen |
| fill |
| |
| circle 170 170 60 |
| defhsla CustomBlue 200 0.7 0.5 1 |
| setcolor CustomBlue |
| fill |
| @end example |
| |
| The commands @vgscmd{setrgba} and @vgscmd{sethsla} allow setting colors using |
| expressions. Similar to @vgscmd{defrgba} and @vgscmd{defhsla}, but with no |
| intermediate variable. |
| |
| @subsection Constants |
| |
| The argument for @vgscmd{setlinecap} and @vgscmd{setlinejoin} is an |
| identifier referring to a constant value. |
| |
| @example |
| setlinecap round |
| @end example |
| |
| @chapter Guide |
| |
| @section 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 @vgscmd{stroke} or @vgscmd{fill}. |
| |
| The |
| @uref{https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths,tutorial |
| on paths in MDN} is a good introduction to the topic. It is focused on |
| @svgpathlink{}, but the same concepts can be applied in drawvg. |
| |
| @anchor{current point} |
| @subsection Current Point |
| |
| Some commands require a @emph{current point}. Initially, the |
| @emph{current point} is set to |
| @uref{https://en.wikipedia.org/wiki/NaN,@code{NaN}}. It is initialized |
| with @vgscmd{M} or @vgscmd{moveto}. Other commands, like @vgscmd{lineto} or |
| @vgscmd{curveto}, updates the @emph{current point} to the new end of the |
| shape. |
| |
| The @emph{current point} can be cleared with @vgscmd{newpath}. Commands |
| that clear the path, like @vgscmd{stroke} or @vgscmd{fill}, also clear the |
| @emph{current point}. |
| |
| @codeexample { |
| @vgscmd{rlineto} uses coordinates relative to the @emph{current point}. |
| |
| Given this script: |
| |
| @example |
| moveto 20 100 |
| rlineto 150 -90 |
| rlineto -50 200 |
| closepath |
| stroke |
| @end example |
| |
| These are the coordinates of the @emph{current point} after executing |
| each command: |
| |
| @multitable @columnfractions .5 .5 |
| @headitem Command @tab Current Point |
| @item @code{moveto 20 100} @tab @code{20, 100} |
| @item @code{rlineto 150 -90} @tab @code{170, 10} |
| @item @code{rlineto -10 50} @tab @code{140, 210} |
| @item @code{closepath} @tab @code{20, 100} |
| @end multitable |
| |
| The same script can be written with single-letter aliases: |
| |
| @example |
| M 20 100 l 150 -90 -50 200 z stroke |
| @end example |
| } |
| |
| @subsection Defining a Shape |
| |
| A path is defined by adding lines, curves, or basic shapes. |
| |
| @itemize |
| @item Basic shapes |
| |
| @itemize |
| @item @vgscmd{circle} |
| @item @vgscmd{ellipse} |
| @item @vgscmd{rect} |
| @item @vgscmd{roundedrect} |
| @end itemize |
| |
| @item |
| Lines |
| @itemize |
| @item @vgscmd{M}, @vgscmd{moveto} |
| @item @vgscmd{m}, @vgscmd{rmoveto} |
| @item @vgscmd{H}, @vgscmd{h} |
| @item @vgscmd{V}, @vgscmd{v} |
| @item @vgscmd{L}, @vgscmd{lineto} |
| @item @vgscmd{l}, @vgscmd{rlineto} |
| @item @vgscmd{Z}, @vgscmd{z}, @vgscmd{closepath} |
| @end itemize |
| |
| @item |
| Curves |
| @itemize |
| @item @vgscmd{arc}, @vgscmd{arcn} |
| @item @vgscmd{C}, @vgscmd{curveto}, |
| @item @vgscmd{c}, @vgscmd{rcurveto} |
| @item @vgscmd{Q}, @vgscmd{q} |
| @item @vgscmd{S}, @vgscmd{s} |
| @item @vgscmd{T}, @vgscmd{t} |
| @end itemize |
| |
| @end itemize |
| |
| Single-letter commands are taken from @svgpathlink{}. |
| |
| @anchor{fill rules} |
| @subsection Fill |
| |
| The region within the shape defined by a path can be filled with |
| @vgscmd{fill} or @vgscmd{eofill}. Each command uses a different |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-fill-rule-t,fill |
| rule}: |
| |
| @itemize |
| @item |
| @vgscmd{fill} uses the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-WINDING:CAPS,winding |
| rule}, also known as |
| @uref{https://en.wikipedia.org/wiki/Nonzero-rule,nonzero rule}. |
| @item |
| @vgscmd{eofill} uses the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS,even--odd |
| rule}. |
| @end itemize |
| |
| @codeexample{ |
| This script shows the difference between the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-WINDING:CAPS,winding} |
| and |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS,even--odd} |
| rules: |
| |
| @example |
| rect 50 10 100 60 |
| circle 150 70 40 |
| setcolor seagreen |
| fill |
| |
| rect 50 130 100 60 |
| circle 150 190 40 |
| setcolor skyblue |
| eofill |
| @end example |
| } |
| |
| @subsection Stroke |
| |
| @vgscmd{stroke} draws a line around the shape defined by the path. The |
| stroke can be configured with different commands: |
| |
| @itemize |
| @item @vgscmd{setdash} |
| @item @vgscmd{setdashoffset} |
| @item @vgscmd{setlinecap} |
| @item @vgscmd{setlinejoin} |
| @item @vgscmd{setlinewidth} |
| @item @vgscmd{resetdash} |
| @end itemize |
| |
| @codeexample{ |
| This example use @vgscmd{setdashoffset} to animate the stroke: |
| |
| @example |
| moveto 0 0 |
| lineto w h |
| |
| setlinecap round |
| setdash 50 50 |
| setlinewidth 20 |
| setdashoffset (hypot(w, h) * t / -3) |
| setcolor seagreen |
| |
| stroke |
| @end example |
| } |
| |
| @subsection Clip |
| |
| A @uref{https://en.wikipedia.org/wiki/Clipping_(computer_graphics),clip |
| region} can be established with @vgscmd{clip} and @vgscmd{eoclip}. |
| |
| If there is an active clip region, the new clip region will be the |
| intersection between the existing one and the path. @vgscmd{resetclip} |
| reset the clip region to the whole frame. |
| |
| @vgscmd{eoclip} uses the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS,even--odd |
| rule} to compute the clip region. |
| |
| @codeexample{ |
| @example |
| 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 |
| @end example |
| } |
| |
| @subsection Preserving Paths |
| |
| The path is cleared after any operation on it, like @vgscmd{fill} or |
| @vgscmd{stroke}. To reuse the same path in multiple operations, |
| @vgscmd{preserve} must be called before them. |
| |
| @codeexample{ |
| In this example, each path is used twice. |
| |
| @example |
| circle 120 120 50 |
| setcolor seagreen |
| preserve stroke |
| clip |
| |
| circle 100 100 50 |
| setcolor skyblue |
| preserve fill |
| setcolor tomato |
| stroke |
| @end example |
| } |
| |
| @section Variables |
| |
| A drawvg can use some variables, provided by the interpreter, to compute |
| values in @ffexprs{}: |
| |
| @table @var |
| @item cx |
| X coordinate of the @ref{current point}. |
| |
| @item cy |
| Y coordinate of the @ref{current point}. |
| |
| @item w |
| Width, in pixels, of the frame. |
| |
| @item h |
| Height, in pixels, of the frame. |
| |
| @item i |
| The loop counter in repeat blocks. |
| |
| @item n |
| Frame number. |
| |
| @item t |
| Timestamp, in seconds. |
| |
| @item ts |
| Timestamp, in seconds, of the first frame. |
| |
| @item duration |
| Duration, in seconds, of the frame. |
| @end table |
| |
| @anchor{User Variables} |
| @subsection User Variables |
| |
| New variables can be created with the @vgscmd{setvar} command. It |
| associates a name with a numeric value. |
| |
| The name must follow these rules: |
| |
| @itemize |
| @item |
| It must start with an ASCII letter or an underscore (@code{_}). |
| |
| @item |
| It can contain only ASCII letters, underscores, and digits. |
| |
| @item |
| It must not match the name of a variable provided by the interpreter |
| (like @var{w} or @var{t}). |
| @end itemize |
| |
| The same variable can be assigned multiple times. |
| |
| @codeexample{ |
| In this example, the result of an expression is stored in a variable |
| with the name @var{progress}. Then, it is used for the @var{x} and |
| @var{width} arguments of @vgscmd{rect}. |
| |
| @example |
| setvar progress (w * (pow(mod(t / 2 + 0.5, 1), 2.5))) |
| |
| rect ((w - progress) / 2) 0 progress h |
| |
| setcolor darkblue |
| fill |
| @end example |
| } |
| |
| Currently, a script can contain only 20 different variable names, but |
| this limit can be modified in the future. |
| |
| @anchor{current pattern} |
| @section Patterns |
| |
| The pattern for fill and stroke operations can be either a solid color, |
| or a gradient. |
| |
| @itemize |
| @item Solid colors. |
| |
| @itemize |
| @item @vgscmd{setcolor} |
| @item @vgscmd{sethsla} |
| @item @vgscmd{setrgba} |
| @end itemize |
| |
| @item Gradients. |
| |
| @itemize |
| @item @vgscmd{lineargrad} |
| @item @vgscmd{radialgrad} |
| @end itemize |
| |
| @end itemize |
| |
| 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. |
| |
| @subsection Gradients |
| |
| To configure a gradient, first call to @vgscmd{lineargrad} or |
| @vgscmd{radialgrad}, and then add color stops by calling @vgscmd{colorstop} |
| for each stop. |
| |
| @codeexample{ |
| In this example, the whole frame is filled with a linear gradient: |
| |
| @example |
| lineargrad 0 0 w h |
| colorstop 0 skyblue |
| colorstop 1 darkblue |
| |
| rect 0 0 w h |
| fill |
| @end example |
| |
| In this example, a radial gradient is used to simulate a sphere: |
| |
| @example |
| 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 |
| @end example |
| } |
| |
| @subsection Variables |
| |
| @vgscmd{setcolor} and @vgscmd{colorstop} accept a variable name as the |
| argument. The variable must be assigned with @vgscmd{defrgba}, |
| @vgscmd{defhsla}, or @vgscmd{setvar} and a color. |
| |
| @codeexample{ |
| @example |
| // Use color #1020FF, alpha = 50% |
| setvar someblue #1020FF@@0.5 |
| |
| setcolor someblue |
| |
| rect 30 30 120 120 |
| fill |
| |
| rect 90 90 120 120 |
| fill |
| @end example |
| } |
| |
| If a variable has the same name of a @ref{Color,,known color,ffmpeg-utils}, the |
| variable has preference, and will be used instead of the predefined color. |
| |
| @codeexample{ |
| @example |
| setcolor teal |
| rect 30 30 120 120 |
| fill |
| |
| setvar teal #70AAAA |
| setcolor teal // Use the new color for `teal`. |
| rect 90 90 120 120 |
| fill |
| @end example |
| } |
| |
| @vgscmd{defrgba} and @vgscmd{defhsla} assign a color to a variable, by providing |
| an expression for each color component: |
| |
| @itemize |
| @item |
| For @vgscmd{defrgba}: @emph{red}, @emph{green}, @emph{blue}, and |
| @emph{alpha}. |
| |
| @item |
| For @vgscmd{defhsla}: @emph{hue}, @emph{saturation}, @emph{lightness}, and |
| @emph{alpha}. |
| @end itemize |
| |
| Each color component must be in range @code{0} to @code{1}, except |
| @emph{hue}, which is @code{0} to @code{360}. |
| |
| @codeexample{ |
| @example |
| 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 |
| @end example |
| } |
| |
| @anchor{transformation matrix} |
| @section Transformations |
| |
| The coordinates for each command can be scaled, rotated, and translated, |
| by using the following commands: |
| |
| @itemize |
| @item @vgscmd{rotate} |
| @item @vgscmd{scale} |
| @item @vgscmd{scalexy} |
| @item @vgscmd{translate} |
| @end itemize |
| |
| 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 |
| @uref{https://www.cairographics.org/manual/cairo-Transformations.html,current |
| transformation matrix} in the Cairo context. To reset the matrix to its |
| original state, before any transformation, use @vgscmd{resetmatrix}. |
| |
| The transform origin for scale and rotation is initially at @code{0, 0}, |
| but it can be adjusted with @vgscmd{translate}. |
| |
| @codeexample{ |
| @example |
| // 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 |
| @end example |
| } |
| |
| @anchor{State Stack} |
| @section 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 @vgscmd{save} command pushes a snapshot of the state to an internal |
| stack. Later, @vgscmd{restore} pops the latest snapshot from the stack, |
| and uses it as the new state. |
| |
| The parameters that can be saved and restored are: |
| |
| @itemize |
| @item |
| Pattern for stroke and fill operations. |
| |
| @itemize |
| @item @vgscmd{lineargrad} |
| @item @vgscmd{radialgrad} |
| @item @vgscmd{setrgba} |
| @item @vgscmd{setcolor} |
| @item @vgscmd{sethsla} |
| @end itemize |
| |
| @item Transformation matrix. |
| |
| @itemize |
| @item @vgscmd{resetmatrix} |
| @item @vgscmd{rotate} |
| @item @vgscmd{scale} |
| @item @vgscmd{scalexy} |
| @item @vgscmd{translate} |
| @end itemize |
| |
| @item Stroke configuration. |
| |
| @itemize |
| @item @vgscmd{setdash} |
| @item @vgscmd{setdashoffset} |
| @item @vgscmd{setlinecap} |
| @item @vgscmd{setlinejoin} |
| @item @vgscmd{setlinewidth} |
| @end itemize |
| |
| @item |
| Clip region |
| |
| @itemize |
| @item @vgscmd{clip} |
| @item @vgscmd{resetclip} |
| @end itemize |
| |
| @end itemize |
| |
| @anchor{Frame Metadata} |
| @section Frame Metadata |
| |
| Some FFmpeg filters add metadata to frames. The command |
| @vgscmd{getmetadata} can read metadata items containing a numeric value, |
| and store it in a variable that can be used for command arguments. |
| |
| @codeexample{ |
| The @code{cropdetect} filter computes the parameters to remove empty |
| regions around the video. These parameters are accessible in the |
| @code{lavfi.cropdetect} keys of the frame metadata. |
| |
| @example |
| // 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 |
| @end example |
| |
| To test the script, copy it to a @code{drawcropdetect.vgs} file, and |
| then execute a command like this: |
| |
| @example |
| ffplay -i example-video.webm -vf 'cropdetect, drawvg=file=drawcropdetect.vgs' |
| @end example |
| } |
| |
| @section @code{if} / @code{repeat} Statements |
| |
| There is limited support for control flow statements: only @vgscmd{if} and |
| @vgscmd{repeat}. |
| |
| Both commands receive two arguments: an expression and a block. |
| |
| @example |
| if (condition) @{ |
| // commands |
| @} |
| |
| repeat (count) @{ |
| // commands |
| @} |
| @end example |
| |
| @vgscmd{if} executes its block if the result of @code{(condition)} is not |
| zero. |
| |
| @vgscmd{repeat} executes its block the number of times specified by |
| @code{(count)}. In each iteration, the variable @var{i} is used as a |
| @uref{https://en.wikipedia.org/wiki/For_loop#Loop_counters,loop |
| counter}. |
| |
| If the result of the expression is not a finite number (like |
| @uref{https://en.wikipedia.org/wiki/NaN,@code{NaN}}) the block is not |
| executed. |
| |
| @anchor{comp-operators} |
| @subsection Comparison and Logical Operators |
| |
| @ffexprs{} only supports arithmetic operators (like @code{+} for addition). |
| Comparison operators (like @code{!=}) are supported via functions, while |
| logical operators (like @code{&&} for @code{AND}) can be emulated with |
| arithmetic operations. |
| |
| @multitable @columnfractions .5 .5 |
| @headitem Expression @tab FFmpeg Equivalent |
| @item @code{x = y} @tab @code{eq(x, y)} |
| @item @code{x < y} @tab @code{lt(x, y)} |
| @item @code{x > y} @tab @code{gt(x, y)} |
| @item @code{x ≤ y} @tab @code{lte(x, y)} |
| @item @code{x ≥ y} @tab @code{gte(x, y)} |
| @item @code{a ≤ x ≤ b} @tab @code{between(x, a, b)} |
| @end multitable |
| |
| Logical operators can be emulated with multiplication (for @code{AND}), |
| or addition (for @code{OR}): |
| |
| @multitable @columnfractions .5 .5 |
| @headitem Expression @tab FFmpeg Equivalent |
| @item @code{x OR y} @tab @code{x + y} |
| @item @code{x AND y} @tab @code{x * y} |
| @end multitable |
| |
| @codeexample{ |
| In other programming languages, a code like this: |
| |
| @example |
| if (x > y && z != 1) @{ |
| // … |
| @} |
| @end example |
| |
| Can be written for drawvg like this: |
| |
| @example |
| if (gt(x, y) * not(eq(z, 1))) @{ |
| // … |
| @} |
| @end example |
| } |
| |
| @subsection Early Exit |
| @vgscmd{break} causes a @vgscmd{repeat} loop to be terminated immediately. |
| |
| If it is executed outside a @vgscmd{repeat} block, it terminates the whole |
| script, or the current procedure. |
| |
| @codeexample{ |
| In this example, we are using the @ref{func-randomg,@code{randomg}} function |
| to draw a line with random segments. |
| |
| The loop can be executed @code{500} times, but it is interrupted if the X |
| coordinate of the @ref{current point} (@var{cx}) exceeds the frame width |
| (@var{w}). The @ref{current point} is updated after each call to |
| @vgscmd{rlineto}. |
| |
| @example |
| moveto 0 0 |
| |
| repeat 500 @{ |
| rlineto |
| (randomg(0) * 15) |
| (randomg(0) * 20) |
| |
| if (gt(cx, w)) @{ |
| break |
| @} |
| @} |
| |
| stroke |
| @end example |
| } |
| |
| @anchor{Procedures} |
| @section Procedures |
| |
| A procedure is a name associated with a block that can be executed |
| multiple times. It can take between 0 and 6 parameters. |
| |
| @vgscmd{proc} is used to set the parameter names and the block for a |
| procedure: |
| |
| @example |
| proc p0 @{ |
| // … |
| @} |
| |
| proc p1 param1 param2 @{ |
| // … |
| @} |
| @end example |
| |
| Inside the block, the arguments can be accessed as regular variables: |
| |
| @example |
| proc square center_x center_y side @{ |
| rect |
| (center_x - side / 2) (center_y - side / 2) |
| side side |
| @} |
| @end example |
| |
| @vgscmd{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 @vgscmd{proc}. |
| |
| @example |
| call p0 |
| |
| call p1 1 2 |
| |
| call square (w / 2) (h / 2) (w / t) |
| @end example |
| |
| @codeexample{ |
| In this example, the procedure @code{zigzag} draws multiple lines from |
| the @ref{current point}. |
| |
| @example |
| 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 |
| @end example |
| |
| The color and the Y coordinate of the starting point can be sent as |
| procedure arguments: |
| |
| @example |
| 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 #40C0FF 60 |
| call zigzag #00AABB 120 |
| call zigzag #20F0B7 180 |
| @end example |
| } |
| |
| 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 @vgscmd{setvar}, @vgscmd{getmetadata}, @vgscmd{defhsla}, |
| and @vgscmd{defrgba}) are preserved. |
| |
| @codeexample{ |
| In the next example, the variable @var{A} has the value @code{0} before |
| calling the procedure @var{P}. During the execution of @var{P}, |
| @code{A} is @code{1}, but after it, @var{A} is @code{0} again. |
| |
| @example |
| setvar A 0 |
| |
| proc P A @{ |
| print A |
| @} |
| |
| print A |
| call P 1 |
| print A |
| @end example |
| |
| It writes the following messages: |
| |
| @verbatim |
| [7:7] A = 0.000 |
| [4:8] A = 1.000 |
| [9:7] A = 0.000 |
| @end verbatim |
| } |
| |
| @vgscmd{break} causes the script to leave the current procedure, similar |
| to the |
| @uref{https://en.wikipedia.org/wiki/Return_statement,@code{return} |
| statement} in other programming languages, unless it is called within a |
| @vgscmd{repeat} loop. |
| |
| The body of the procedure must be defined with @vgscmd{proc} @emph{before} |
| using @vgscmd{call}. |
| |
| @codeexample{ |
| In this example, when the procedure @code{notyet} is called, its body |
| has not yet defined, so the execution fails with the error |
| @code{Missing body for procedure 'notyet'}. |
| |
| @example |
| call notyet |
| |
| proc notyet @{ |
| // ... |
| @} |
| @end example |
| } |
| |
| A procedure can be redefined by other calls to @vgscmd{proc} with the same |
| name. In such case, @vgscmd{call} invokes the last assigned block. |
| |
| @codeexample{ |
| In this example, the procedure @code{example} has two different blocks. |
| |
| @example |
| proc example @{ |
| // block1 |
| @} |
| |
| call example // executes block1 |
| |
| proc example @{ |
| // block2 |
| @} |
| |
| call example // executes block2 |
| @end example |
| } |
| |
| @section Functions in Expressions |
| |
| There are some functions specific to drawvg available in @ffexprs{}. |
| |
| @subsection Function @code{p} |
| |
| @code{p(x, y)} returns the color of the pixel at coordinates @code{x, y}, as a |
| @code{0xRRGGBBAA} value. It can be assigned to a variable, so the color can be |
| available for @vgscmd{setcolor} and @vgscmd{colorstop} commands. |
| |
| If a single expression contains multiple calls to the function, it must return |
| the value of the last call in order to use it as a color. |
| |
| @codeexample{ |
| In this example, the first call to @code{p(0, 0)} is stored in the variable |
| @var{0} of the expression. Then, the same expression makes a second call to |
| @code{p(1, 1)}, and finally it returns the value in the variable @var{0}. |
| |
| @example |
| setvar pixel (st(0, p(0, 0)); p(1, 1); ld(0)) |
| @end example |
| |
| Since the result of the expression is not the last call to @code{p}, the |
| variable @var{pixel} can not be used as a color, but it still can be used as |
| a numeric @code{0xRRGGBBAA} value. |
| } |
| |
| If the coordinates are outside the frame, or any of the arguments is not |
| a finite number (like |
| @uref{https://en.wikipedia.org/wiki/NaN,@code{NaN}}), the function |
| returns @code{NaN}. |
| |
| The @ref{transformation matrix} is applied to the |
| arguments. To use the original frame coordinates, call |
| @vgscmd{resetmatrix} between @vgscmd{save} and @vgscmd{restore}: |
| |
| @example |
| save |
| resetmatrix |
| setvar pixel (p(0, 0)) // top-left pixel of the frame. |
| restore |
| |
| setcolor pixel |
| @end example |
| |
| Bitwise operations can be used to extract individual color components: |
| |
| @example |
| 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)) |
| @} |
| @end example |
| |
| @subsection Function @code{pathlen} |
| |
| @code{pathlen(n)} computes the length of the current path, by adding the |
| length of each line segment returned by |
| @uref{https://www.cairographics.org/manual/cairo-Paths.html#cairo-copy-path-flat,@code{cairo_copy_path_flat}}. |
| |
| The function expects an argument @var{n}, as the maximum number of line |
| segments to add to the length, or @code{0} to add all segments. |
| |
| @codeexample{ |
| In this example, @code{pathlen} is used to animate the stroke of a |
| spiral, in a 5 seconds loop. |
| |
| @example |
| 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 |
| @end example |
| } |
| |
| @anchor{func-randomg} |
| @subsection Function @code{randomg} |
| |
| @code{randomg(idx)} is similar to the @code{random(idx)} function, |
| available in @ffexprs{}, but its state is global to the frame, instead |
| of specific to each expression. |
| |
| To understand the difference, we need to dive into how |
| @code{random(idx)} works inside a drawvg script. |
| |
| First, each expression in FFmpeg has a set of 10 internal variables, |
| which can be written with @code{st(idx, value)}, and can be read with |
| @code{ld(idx)}. @var{idx} is a value between @code{0} and @code{9}. |
| These variables are initialized to @code{0}. |
| |
| When a drawvg script is parsed, each expression is compiled with |
| @uref{https://ffmpeg.org/doxygen/8.0/eval_8h.html#ad3bf8f3330d1fd139de2ca156c313f34,@code{av_expr_parse}}, |
| from @uref{https://ffmpeg.org/libavutil.html,libavutil}, and these |
| compiled expressions are reused for every frame. The changes in the |
| internal variables (with @code{st(idx, value)}) are visible between |
| frames, but they are not shared between expressions. |
| |
| @codeexample{ |
| In this example, the expression for the X coordinate updates its |
| internal variable @code{0} in every frame: |
| |
| @example |
| circle |
| (st(0, mod(ld(0) + 15, w))) // X |
| 120 // Y |
| (ld(0) + 20) // radius |
| |
| fill |
| @end example |
| |
| @code{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 |
| @code{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: |
| |
| @example |
| circle (mod(n * 15, w)) 120 20 |
| fill |
| @end example |
| } |
| |
| The function @code{random(idx)} returns a |
| @uref{https://en.wikipedia.org/wiki/Pseudorandom_number_generator,pseudorandom} |
| value between @code{0} and @code{1}. @var{idx} is the internal variable |
| that is used both as the seed and to keep the state of the number |
| generator. |
| |
| @codeexample{ |
| The next example uses @code{random(0)} to generate a random value for |
| the center of a circle: |
| |
| @example |
| circle |
| (random(0) * w) |
| (random(0) * h) |
| 10 |
| |
| fill |
| @end example |
| |
| 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 @code{0xF0F0}) to the internal variable of one of expressions, but |
| only when its value is @code{0}: |
| |
| @example |
| circle |
| (random(0) * w) |
| (st(0, if(ld(0), ld(0), 0xF0F0)); random(0) * h) |
| 10 |
| |
| fill |
| @end example |
| |
| 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: |
| |
| @example |
| 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 |
| @end example |
| |
| This is achieved by using a precomputed state before calling @code{random(0)}. |
| The variable @var{i}, updated by @vgscmd{repeat}, is needed to compute |
| different states in each iteration. |
| } |
| |
| The @code{randomg(idx)} function, which is specific to drawvg scripts, |
| is similar to @code{random(idx)}, but intended to solve the previous |
| problems: |
| |
| @itemize |
| @item All frames have the same seed. |
| @item The state is shared between expressions. |
| @end itemize |
| |
| The parameter @var{idx} has two uses: |
| |
| @itemize |
| @item |
| The last two bits are the index of an internal state, so it is possible |
| to have 4 different number generators. |
| |
| @item |
| The first call to @code{randomg} with a specific index will use the |
| argument as the seed for the number generator in that index. |
| @end itemize |
| |
| In a script like this: |
| |
| @example |
| M (randomg(0xFF1)) (randomg(0xFF0)) |
| l (randomg(0xAA1)) (randomg(0xFF0)) |
| @end example |
| |
| There are 4 calls to @code{randomg}: |
| |
| @enumerate |
| @item |
| The first call, with the argument @code{0xFF1}, uses the internal state |
| at index @code{1} (because @code{0xFF1} modulo @code{4} is @code{1}). |
| |
| Since this is the first use of that index, the number generator is |
| initialized with the seed @code{0xFF1}. |
| |
| @item |
| The second call has the same behaviour: it initializes the state at |
| index @code{0} with the value @code{0xFF0}. |
| |
| @item |
| The third call has the argument @code{0xAA1}, and it uses index |
| @code{1}. Since that state is already initialized (with the seed |
| @code{0xFF1}), the value @code{0xAA1} is ignored, and it returns the |
| next number. |
| |
| @end enumerate |
| |
| @codeexample{ |
| This example renders a simple rain animation, moving lines from top to |
| bottom. |
| |
| @code{randomg} is used to distribute the lines over the frame, and to |
| apply different speeds to each one. |
| |
| @example |
| 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 |
| @} |
| @end example |
| } |
| |
| @section Tracing with @code{print} |
| |
| It is possible to trace the execution of a drawvg script by printing the |
| value of an expression, either with the @vgscmd{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. |
| |
| @subsection Function print |
| |
| The function @code{print(t)} writes the value of t, and returns its |
| argument. |
| |
| @codeexample{ |
| Given a line line this: |
| |
| @example |
| M (sin(2 * PI * t) * w) 0 |
| @end example |
| |
| We can see the values of @code{sin(2 * PI * t)} by surrounding it with a |
| call to @code{print()}: |
| |
| @example |
| M (print(sin(2 * PI * t)) * w) 0 |
| @end example |
| |
| Executing this script with a 1 second / 8 FPS video shows the expected |
| values for the sine function. |
| |
| @verbatim |
| $ 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 |
| @end verbatim |
| } |
| |
| @anchor{Command print} |
| @subsection Command @code{print} |
| |
| The command @vgscmd{print} accepts an arbitrary number of arguments, and |
| for each one it writes: |
| |
| @itemize |
| @item |
| The source location (line and column). |
| @item |
| The source code of the expression. |
| @item |
| The result of evaluating the expression. |
| @end itemize |
| |
| When there are multiple expressions, they are separated by the @code{|} |
| character. |
| |
| @codeexample{ |
| The next script prints the position of the @ref{current point} after the |
| @vgscmd{l} command: |
| |
| @example |
| M 10 20 |
| l 100 100 |
| print cx cy |
| stroke |
| @end example |
| |
| For each frame, it produces this output: |
| |
| @verbatim |
| [3:7] cx = 110.000000 | [3:10] cy = 120.000000 |
| @end verbatim |
| |
| The next example prints the values of @code{random(0)}: |
| |
| @verbatim |
| $ 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 |
| @end verbatim |
| } |
| |
| @chapter Commands |
| |
| @macro signature {sig} |
| @b{@code{\sig\}} |
| @end macro |
| |
| @macro signatureimpl {sig} |
| @signature{\sig\} @ @ @ --- @ref{implicit commands,@i{Can be implicit}} |
| @end macro |
| |
| @anchor{cmd_arc} |
| @section @code{arc} |
| |
| @signatureimpl{arc @var{xc} @var{yc} @var{radius} @var{angle1} @var{angle2}} |
| |
| Adds a circular arc of the given @var{radius} to the current path. The |
| arc is centered at @var{xc, yc}, begins at @var{angle1} and proceeds |
| in the direction of increasing angles to end at @var{angle2}. |
| |
| If there is a @ref{current point}, a line is added from it to the beginning of |
| the arc. If this is not desired, use @vgscmd{newpath} before @vgscmd{arc} to clear |
| the @ref{current point}. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-Paths.html#cairo-arc,@code{cairo_arc}} |
| function for more details. |
| |
| @codeexample{ |
| @example |
| arc 120 120 60 0 (3 * PI / 2) |
| stroke |
| @end example |
| } |
| |
| @anchor{cmd_arcn} |
| @section @code{arcn} |
| |
| @signatureimpl{arcn @var{xc} @var{yc} @var{radius} @var{angle1} @var{angle2}} |
| |
| Similar to @vgscmd{arc}, but it differs in the direction of the arc |
| between the two angles. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-Paths.html#cairo-arc-negative,@code{cairo_arc_negative}} |
| function for more details. |
| |
| @codeexample{ |
| In this example, both @vgscmd{arc} and @vgscmd{arcn} have the same angles, |
| but they render different arcs: |
| |
| @example |
| arc 120 90 60 (PI / 2) 0 |
| |
| newpath |
| arcn 120 150 60 (PI / 2) 0 |
| |
| stroke |
| @end example |
| } |
| |
| @vgscmd{newpath} is needed to prevent a line between the two arcs. |
| |
| @anchor{cmd_break} |
| @section @code{break} |
| |
| @signature{break} |
| |
| @vgscmd{break} terminates the execution of the innermost block, either a |
| @vgscmd{repeat} loop or a procedure. |
| |
| If it is used outside of a @vgscmd{repeat} / @vgscmd{proc} block, it |
| terminates the script for the current frame. |
| |
| @anchor{cmd_call} |
| @section @code{call} |
| |
| @signature{call @var{name} @var{args}*} |
| |
| Invokes a procedure defined by @vgscmd{proc}. |
| |
| See the @ref{Procedures} section above for more details. |
| |
| @anchor{cmd_circle} |
| @section @code{circle} |
| |
| @signatureimpl{circle @var{xc} @var{yc} @var{radius}} |
| |
| Adds a circle of the given @var{radius} to the current path. The circle |
| is centered at @var{xc, yc}. The @ref{current point} is cleared before and |
| after adding the circle. |
| |
| This is a convenience wrapper for @vgscmd{arc}. A call to @vgscmd{circle} is |
| equivalent to: |
| |
| @example |
| newpath |
| arc xc yc radius (0) (2 * PI) |
| newpath |
| @end example |
| |
| @anchor{cmd_clip} |
| @anchor{cmd_eoclip} |
| @section @code{clip}, @code{eoclip} |
| |
| @signature{clip, eoclip} |
| |
| Establishes a new clip region by intersecting the current clip region |
| with the current path as it would be filled by @vgscmd{fill} or |
| @vgscmd{eofill}. |
| |
| @vgscmd{eoclip} uses the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS,even--odd |
| rule}. See @ref{fill rules} for more details. |
| |
| The path is cleared after updating the clip region, unless the |
| @vgscmd{preserve} command is used before @vgscmd{clip} or @vgscmd{eoclip}. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-clip,@code{cairo_clip}} |
| function for more details. |
| |
| @anchor{cmd_Z} |
| @anchor{cmd_z} |
| @anchor{cmd_closepath} |
| @section @code{Z}, @code{z}, @code{closepath} |
| |
| @signature{Z, z, closepath} |
| |
| Adds a line segment to the path from the @ref{current point} to the beginning |
| of the current sub-path, and closes this sub-path. The beginning is set by any |
| of the @emph{move} commands (@vgscmd{M}, @vgscmd{m}, @vgscmd{moveto}, |
| @vgscmd{rmoveto}). |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-Paths.html#cairo-close-path,@code{cairo_close_path}} |
| function for more details. |
| |
| @anchor{cmd_colorstop} |
| @section @code{colorstop} |
| |
| @signatureimpl{colorstop @var{offset} @var{color}} |
| |
| Adds a color stop to a gradient pattern. |
| |
| @var{offset} is a value between @code{0} and @code{1}, and it specifies |
| the location along the gradient's control vector. |
| |
| This command must be executed after @vgscmd{lineargrad} or |
| @vgscmd{radialgrad}. |
| |
| Color stops can be added in any number of calls to @vgscmd{colorstop}. In |
| the next example, the 3 blocks define the same gradient: |
| |
| @example |
| // 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 |
| @end example |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-pattern-t.html#cairo-pattern-add-color-stop-rgba,@code{cairo_pattern_add_color_stop_rgba}} |
| function for more details. |
| |
| @codeexample{ |
| In this example, color stops are added in a @vgscmd{repeat} loop. |
| |
| @example |
| 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 |
| @end example |
| |
| It is possible to avoid transitions between color stops by repeating the |
| same color in two stops: |
| |
| @example |
| 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 |
| @end example |
| } |
| |
| @anchor{cmd_C} |
| @anchor{cmd_curveto} |
| @section @code{C}, @code{curveto} |
| |
| @signatureimpl{C, curveto @var{x1} @var{y1} @var{x2} @var{y2} @var{x} @var{y}} |
| |
| Draw a cubic Bézier curve from the @ref{current point} to the @emph{end point} |
| specified by @var{x, y}. The @emph{start control point} is specified by |
| @var{x1, y1} and the @emph{end control point} is specified by @var{x2, y2}. |
| |
| @macro mdncubicbeziercurve |
| @uref{https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/d#cubic_b%C3%A9zier_curve,Cubic Bézier Curve on MDN} |
| @end macro |
| |
| @macro mdntutorialcurve |
| @uref{https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths#curve_commands,Curve Commands section of the Paths tutorial on MDN} |
| @end macro |
| |
| The behaviour is identical to the @vgscmd{C} command in @svgpathlink{}. For more |
| details, see @mdncubicbeziercurve{}, and the @mdntutorialcurve{}. |
| |
| @codeexample{ |
| @example |
| moveto 20 20 |
| |
| curveto |
| 0 (h / 2) // start control point |
| w (h / 2) // end control point |
| (w - 20) (h - 20) // end point |
| |
| stroke |
| @end example |
| } |
| |
| @anchor{cmd_c} |
| @anchor{cmd_rcurveto} |
| @section @code{c}, @code{rcurveto} |
| |
| @signatureimpl{c, rcurveto @var{dx1} @var{dy1} @var{dx2} @var{dy2} @var{dx} @var{dy}} |
| |
| Like @vgscmd{curveto}, but the coordinates are relative to the @ref{current |
| point}. |
| |
| @anchor{cmd_defhsla} |
| @section @code{defhsla} |
| |
| @signature{defhsla varname @var{h} @var{s} @var{l} @var{a}} |
| |
| Similar to @vgscmd{sethsla}, but instead of establishing the color for |
| stroke and fill operations, the computed color is assigned to the |
| variable @var{varname}. |
| |
| @var{varname} can then be used as a color for @vgscmd{setcolor} and |
| @vgscmd{colorstop}. |
| |
| See @vgscmd{sethsla} for more details on how the color is computed. |
| |
| @anchor{cmd_defrgba} |
| @section @code{defrgba} |
| |
| @signature{defrgba varname @var{r} @var{g} @var{b} @var{a}} |
| |
| Computes a color from the @emph{red}, @emph{green}, @emph{blue}, and |
| @emph{alpha} components, and assigns it to the variable @var{varname}. |
| |
| All components are values between @code{0} and @code{1}. Values outside |
| that range are clamped to it. |
| |
| @anchor{cmd_ellipse} |
| @section @code{ellipse} |
| |
| @signatureimpl{ellipse @var{cx} @var{cy} @var{rx} @var{ry}} |
| |
| Adds an ellipse to the current path. Similar to @vgscmd{circle}, but it is |
| possible to use different radius for both axes. |
| |
| @codeexample{ |
| @verbatim |
| ellipse 120 120 75 50 |
| stroke |
| @end verbatim |
| } |
| |
| @anchor{cmd_fill} |
| @anchor{cmd_eofill} |
| @section @code{fill}, @code{eofill} |
| |
| @signature{fill, eofill} |
| |
| Fill the current path, using the @ref{current pattern} (either |
| a solid color or a gradient). |
| |
| @vgscmd{eofill} uses the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#CAIRO-FILL-RULE-EVEN-ODD:CAPS,even--odd |
| rule}. See @ref{fill rules} for more details. |
| |
| The path is cleared after the operation, unless the @vgscmd{preserve} |
| command is used before @vgscmd{fill} or @vgscmd{eofill}. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-fill,@code{cairo_fill}} |
| function for more details. |
| |
| @anchor{cmd_getmetadata} |
| @section @code{getmetadata} |
| |
| @signature{getmetadata @var{varname} @var{key}} |
| |
| Get the value of a metadata entry created by another filter, and assign |
| it to the variable @var{varname}. |
| |
| If there is no metadata entry for @var{key}, or its value is not a |
| number, @var{varname} is set to |
| @uref{https://en.wikipedia.org/wiki/NaN,@code{NaN}}. |
| |
| See the @ref{Frame Metadata} section above for an |
| example. |
| |
| @anchor{cmd_H} |
| @anchor{cmd_h} |
| @section @code{H}, @code{h} |
| |
| @signatureimpl{H, h @var{x}} |
| |
| Draw a horizontal line from the @ref{current point} to x. |
| |
| The coordinate for @vgscmd{H} is absolute, and for @vgscmd{h} it is relative |
| to the @ref{current point}. |
| |
| @anchor{cmd_if} |
| @section @code{if} |
| |
| @signature{if @var{condition} @{ @var{block} @}} |
| |
| Executes a block if the value of @var{condition} is not zero, and a |
| finite number (unlike |
| @uref{https://en.wikipedia.org/wiki/NaN,@code{NaN}}). |
| |
| See the @ref{comp-operators,Comparison and Logical Operators} section |
| above for more details on how to write conditional expressions. |
| |
| @anchor{cmd_lineargrad} |
| @section @code{lineargrad} |
| |
| @signature{lineargrad @var{x0} @var{y0} @var{x1} @var{y1}} |
| |
| Set the @ref{current pattern} to a new linear gradient, along |
| the line from the coordinates @var{x0, y0} to @var{x1, y1}. |
| |
| This gradient can be used for stroke and fill operations. |
| |
| Use @vgscmd{colorstop} to set the color for each position in the gradient. |
| |
| @anchor{cmd_L} |
| @anchor{cmd_lineto} |
| @section @code{L}, @code{lineto} |
| |
| @signatureimpl{L, lineto @var{x} @var{y}} |
| |
| Draw a line from the @ref{current point} to the coordinates at @var{x, y}. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-Paths.html#cairo-line-to,@code{cairo_line_to}} |
| function for more details. |
| |
| @anchor{cmd_l} |
| @anchor{cmd_rlineto} |
| @section @code{l}, @code{rlineto} |
| |
| @signatureimpl{l, rlineto @var{dx} @var{dy}} |
| |
| Like @vgscmd{lineto}, but the coordinates are relative to the @ref{current |
| point}. |
| |
| @anchor{cmd_M} |
| @anchor{cmd_moveto} |
| @section @code{M}, @code{moveto} |
| |
| @signatureimpl{M, moveto @var{x} @var{y}} |
| |
| Begin a new sub-path, and set the @ref{current point} to @var{x, y}. |
| |
| @anchor{cmd_m} |
| @anchor{cmd_rmoveto} |
| @section @code{m}, @code{rmoveto} |
| |
| @signatureimpl{m, rmoveto @var{dx} @var{dy}} |
| |
| Like @vgscmd{moveto}, but the coordinates are relative to the @ref{current |
| point}. |
| |
| @anchor{cmd_newpath} |
| @section @code{newpath} |
| |
| @signature{newpath} |
| |
| Begin a new sub-path. Like @vgscmd{moveto}, but there is no |
| @ref{current point} after it. |
| |
| @codeexample{ |
| In the next example, @vgscmd{newpath} is used in the path on the right to |
| prevent the line connecting both arcs. |
| |
| @verbatim |
| 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 |
| @end verbatim |
| } |
| |
| @anchor{cmd_preserve} |
| @section @code{preserve} |
| |
| @signature{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: |
| |
| @itemize |
| @item @vgscmd{clip} |
| @item @vgscmd{eoclip} |
| @item @vgscmd{eofill} |
| @item @vgscmd{fill} |
| @item @vgscmd{stroke} |
| @end itemize |
| |
| The script can contain any command between @vgscmd{preserve} and the |
| associated operation. This allows modifying other properties, like the |
| current color. |
| |
| @codeexample{ |
| In this example, the same path is used for both @vgscmd{fill} and |
| @vgscmd{stroke}, but with different colors. |
| |
| @verbatim |
| circle (w / 2) (h / 2) (w / 3) |
| |
| setcolor skyblue |
| preserve fill |
| |
| setlinewidth 10 |
| setcolor seagreen |
| stroke |
| @end verbatim |
| } |
| |
| @vgscmd{preserve} can be called multiple times, if the same path has to be |
| used in 3 or more operations. |
| |
| @codeexample{ |
| In this example, the path created by @vgscmd{circle} is used by |
| @vgscmd{fill}, @vgscmd{stroke}, and @vgscmd{clip}. After @vgscmd{clip}, the |
| path is cleared. |
| |
| @verbatim |
| circle 100 100 50 |
| |
| preserve fill |
| preserve stroke |
| clip |
| @end verbatim |
| } |
| |
| @anchor{cmd_print} |
| @section @code{print} |
| |
| @signatureimpl{print @var{expr}} |
| |
| Print its arguments to the FFmpeg log. |
| |
| See the @ref{Command print} section above for more details. |
| |
| @anchor{cmd_proc} |
| @section @code{proc} |
| |
| @signature{proc @var{name} @var{params}* @{ @var{block} @}} |
| |
| Assign the block and the parameters for the procedure @var{name}. The |
| procedure can be called multiple times with the @vgscmd{call} command. |
| |
| See the @ref{Procedures} section above for more details. |
| |
| @anchor{cmd_Q} |
| @section @code{Q} |
| |
| @signature{Q x1 y1 @var{x} @var{y}} |
| |
| Draw a quadratic Bézier curve from the @ref{current point} to the @emph{end |
| point} specified by @var{x, y}. The @emph{control point} is specified by |
| @var{x1, y1}. |
| |
| @macro mdnquadbeziercurve |
| @uref{https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/d#quadratic_b%C3%A9zier_curve,Quadratic Bézier curve on MDN} |
| @end macro |
| |
| The behaviour is identical to the @code{Q} command in @svgpathlink{}. For more |
| details, see @mdnquadbeziercurve{}, and the @mdntutorialcurve{}. |
| |
| @codeexample{ |
| @verbatim |
| moveto 20 20 |
| |
| Q |
| 0 h // control point |
| (w - 20) (h - 20) // end point |
| |
| stroke |
| @end verbatim |
| } |
| |
| @anchor{cmd_q} |
| @section @code{q} |
| |
| @signature{q @var{dx1} @var{dy1} @var{dx} @var{dy}} |
| |
| Like @vgscmd{Q}, but the coordinates are relative to the @ref{current point}. |
| |
| @anchor{cmd_radialgrad} |
| @section @code{radialgrad} |
| |
| @signature{radialgrad @var{cx0} @var{cy0} @var{radius0} @var{cx1} @var{cy1} @var{radius1}} |
| |
| Creates a new radial gradient between the two circles defined by |
| @var{cx0 cy0 radius0} and @var{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 @vgscmd{colorstop} to set the color for each position in the gradient. |
| |
| @codeexample{ |
| The animation in the next example shows how the two circles defined in |
| the @vgscmd{radialgrad} arguments interact with each other. |
| |
| The red circle represent the circle for the @var{cx0 cy0 radius0} |
| arguments, and the yellow circle is the one for the |
| @var{cx1 cy1 radius1} arguments. |
| |
| @verbatim |
| 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 |
| @end verbatim |
| } |
| |
| @anchor{cmd_rect} |
| @section @code{rect} |
| |
| @signature{rect @var{x} @var{y} @var{width} @var{height}} |
| |
| Adds a rectangle of the given size (@var{width} × @var{height}), at |
| position @var{x, y}, to the current path. The @ref{current point} is cleared |
| before and after adding the rectangle. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-Paths.html#cairo-rectangle,@code{cairo_rectangle}} |
| function for more details. |
| |
| @anchor{cmd_repeat} |
| @section @code{repeat} |
| |
| @signature{repeat @var{count} @{ @var{block} @}} |
| |
| Executes a block the number of times indicated by @var{count}. |
| |
| In each iteration, the variable @var{i} is used as a |
| @uref{https://en.wikipedia.org/wiki/For_loop#Loop_counters,loop |
| counter}. It takes the values from @code{0} to @code{count - 1}. When |
| the loop is terminated, the variable is restored to the value before |
| starting the loop. |
| |
| If @var{count} is less than @code{1}, or it is not a finite number |
| (like @uref{https://en.wikipedia.org/wiki/NaN,@code{NaN}}), the block is |
| not executed. |
| |
| @anchor{cmd_resetclip} |
| @section @code{resetclip} |
| |
| @signature{resetclip} |
| |
| Reset the current clip region to its original state, covering the whole |
| frame. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-reset-clip,@code{cairo_reset_clip}} |
| function for more details. |
| |
| @anchor{cmd_resetdash} |
| @section @code{resetdash} |
| |
| @signature{resetdash} |
| |
| Disable the dash pattern to be used by @vgscmd{stroke}. This reverts any |
| change made by @vgscmd{setdash} and @vgscmd{setdashoffset}. |
| |
| It calls |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash,@code{cairo_set_dash}} |
| with @code{num_dashes} set to @code{0}. |
| |
| @anchor{cmd_resetmatrix} |
| @section @code{resetmatrix} |
| |
| @signature{resetmatrix} |
| |
| Resets the current @ref{transformation matrix}. |
| |
| @anchor{cmd_restore} |
| @section @code{restore} |
| |
| @signature{restore} |
| |
| Restores the state saved by a preceding call to @vgscmd{save}. |
| |
| For more details, see the @ref{State Stack} section above, and the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-restore,@code{cairo_restore}} |
| function. |
| |
| @anchor{cmd_rotate} |
| @section @code{rotate} |
| |
| @signature{rotate @var{angle}} |
| |
| Modifies the current @ref{transformation matrix} by rotating the user-space |
| axes by @var{angle} radians. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-Transformations.html#cairo-rotate,@code{cairo_rotate}} |
| function for more details. |
| |
| @codeexample{ |
| In this example: |
| |
| @itemize |
| @item @vgscmd{scalexy} maps the coordinates to a 1x1 frame. |
| @item @vgscmd{translate} put @code{0, 0} at the center of the frame. |
| @item @vgscmd{rotate} rotates 45°. |
| @item |
| @vgscmd{resetmatrix} reverts the transformations before @vgscmd{stroke}, so the |
| line width is not affected by the scale. |
| @end itemize |
| |
| @verbatim |
| scalexy w h |
| translate 0.5 0.5 |
| rotate (PI / 4) |
| rect -0.25 -0.25 0.5 0.5 |
| resetmatrix |
| stroke |
| @end verbatim |
| } |
| |
| @anchor{cmd_roundedrect} |
| @section @code{roundedrect} |
| |
| @signatureimpl{roundedrect @var{x} @var{y} @var{width} @var{height} @var{radius}} |
| |
| Like @vgscmd{rect}, but a circular arc is used for the corners. |
| |
| @codeexample{ |
| The next example shows the same rectangle, with different values for the |
| corner radius. |
| |
| The radius is computed by multiplying @var{i} (the |
| @uref{https://en.wikipedia.org/wiki/For_loop#Loop_counters,loop counter}) by |
| @code{4.5}. This number is chosen to make the last shape a perfect circle. |
| |
| @example |
| repeat 9 @{ |
| roundedrect |
| (mod(i, 3) * 80 + 5) // x |
| (floor(i / 3) * 80 + 5) // y |
| 70 70 // size |
| (i * 4.5) // radius |
| @} |
| |
| stroke |
| @end example |
| } |
| |
| @anchor{cmd_save} |
| @section @code{save} |
| |
| @signature{save} |
| |
| Saves a copy of the current state on an internal stack. This copy can be |
| restored later with @vgscmd{restore}. |
| |
| For more details, see the @ref{State Stack} section above, and the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-save,@code{cairo_save}} |
| function. |
| |
| @anchor{cmd_scale} |
| @section @code{scale} |
| |
| @signature{scale @var{sxy}} |
| |
| Similar to @vgscmd{scalexy}, but the same value is used for both axes. It |
| is equivalent to: |
| |
| @signature{scalexy @var{sxy} @var{sxy}} |
| |
| @anchor{cmd_scalexy} |
| @section @code{scalexy} |
| |
| @signature{scalexy @var{sx} @var{sy}} |
| |
| Modifies the current @ref{transformation matrix} by scaling the X and Y |
| user-space axes by @var{sx} and @var{sy} respectively. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-Transformations.html#cairo-scale,@code{cairo_scale}} |
| function for more details. |
| |
| See @vgscmd{rotate} for an example on combining multiple transformations. |
| |
| @anchor{cmd_setcolor} |
| @section @code{setcolor} |
| |
| @signature{setcolor @var{color}} |
| |
| Set a solid color as the @ref{current pattern} for stroke and fill operations |
| |
| See the @ref{Colors} section above for more details. |
| |
| @anchor{cmd_setdash} |
| @section @code{setdash} |
| |
| @signatureimpl{setdash @var{length}} |
| |
| Sets the dash pattern to be used by @vgscmd{stroke}. |
| |
| Each call to @vgscmd{setdash} adds a length to the pattern, alternating |
| between @emph{on} and @emph{off} portions of the stroke. |
| |
| After a call to @vgscmd{setdash}, @vgscmd{resetdash} is needed either to |
| create a new pattern, or to discard the current one. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash,@code{cairo_set_dash}} |
| function for more details. |
| |
| @anchor{cmd_setdashoffset} |
| @section @code{setdashoffset} |
| |
| @signature{setdashoffset @var{offset}} |
| |
| Set the offset into the dash pattern at which the stroke should start. |
| |
| @vgscmd{setdash} must be called @emph{before} @vgscmd{setdashoffset}. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash,@code{cairo_set_dash}} |
| function for more details. |
| |
| @codeexample{ |
| The next animation shows the effect of @vgscmd{setdashoffset} when its |
| argument changes over time. |
| |
| @verbatim |
| 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 |
| @end verbatim |
| } |
| |
| @anchor{cmd_sethsla} |
| @section @code{sethsla} |
| |
| @signature{sethsla @var{h} @var{s} @var{l} @var{a}} |
| |
| Set the @ref{current pattern} to a solid color, given the @emph{hue}, |
| @emph{saturation}, and @emph{lightness}, and @emph{alpha} components. |
| |
| h is the @emph{hue}, a value between @code{0} and @code{359}. Negative |
| values are clamped to @code{0}, and values greater than @code{359} are |
| interpreted as modulo 360. |
| |
| s (@emph{saturation}), l (@emph{lightness}), and a (@emph{alpha}), are |
| values between @code{0} and @code{1}. |
| |
| The conversion to RGB is implemented according to the |
| @uref{https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB,formulae from |
| Wikipedia}. |
| |
| @anchor{cmd_setlinecap} |
| @section @code{setlinecap} |
| |
| @signature{setlinecap @var{cap}} |
| |
| Set the current line cap style, which determines the shape used to draw |
| the end points of lines. |
| |
| @var{cap} must be one of the following names: |
| |
| @itemize |
| @item @code{butt} |
| @item @code{round} |
| @item @code{square} |
| @end itemize |
| |
| It calls to |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-cap,@code{cairo_set_line_cap}} |
| to set the line cap style. |
| |
| @codeexample{ |
| This example draws 3 lines with the same length, each one with a |
| different line cap style: |
| |
| @verbatim |
| 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 |
| @end verbatim |
| } |
| |
| @anchor{cmd_setlinejoin} |
| @section @code{setlinejoin} |
| |
| @signature{setlinejoin @var{join}} |
| |
| Sets the current line join style, which determines the shape used to |
| join two line segments. |
| |
| @var{join} must be one of the following names: |
| |
| @itemize |
| @item @code{bevel} |
| @item @code{miter} |
| @item @code{round} |
| @end itemize |
| |
| It calls to |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-join,@code{cairo_set_line_join}} |
| to set the line join style. |
| |
| @codeexample{ |
| This example draws 3 lines with the same length, each one with a |
| different line join style: |
| |
| @verbatim |
| 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 |
| @end verbatim |
| } |
| |
| @anchor{cmd_setlinewidth} |
| @section @code{setlinewidth} |
| |
| @signature{setlinewidth @var{width}} |
| |
| Set the line width for @vgscmd{stroke}. |
| |
| @var{width} is affected by the @ref{transformation matrix}. |
| |
| To specify a width that is not affected by other transformations, |
| @vgscmd{resetmatrix} can be used between @vgscmd{save} / @vgscmd{restore}: |
| |
| @verbatim |
| save |
| |
| resetmatrix |
| setlinewidth 1 |
| stroke |
| |
| // Restore matrix after stroke. |
| restore |
| @end verbatim |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-width,@code{cairo_set_line_width}} |
| function for more details. |
| |
| @anchor{cmd_setrgba} |
| @section @code{setrgba} |
| |
| @signature{setrgba @var{r} @var{g} @var{b} @var{a}} |
| |
| Set the @ref{current pattern} to a solid color, given the |
| @emph{red}, @emph{green}, @emph{blue}, and @emph{alpha} components. |
| |
| All components are values between @code{0} and @code{1}. Values outside |
| that range are clamped to it. |
| |
| @anchor{cmd_setvar} |
| @section @code{setvar} |
| |
| @signature{setvar @var{varname} @var{value}} |
| |
| Set the variable @var{varname} to @var{value}. |
| |
| See the @ref{User Variables} section above for more details. |
| |
| @anchor{cmd_stroke} |
| @section @code{stroke} |
| |
| @signature{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 @vgscmd{preserve} |
| command is used before @vgscmd{stroke}. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-stroke,@code{cairo_stroke}} |
| function for more details. |
| |
| @anchor{cmd_S} |
| @anchor{cmd_s} |
| @section @code{S}, @code{s} |
| |
| @signatureimpl{S, s @var{x2} @var{y2} @var{x} @var{y}} |
| |
| Draw a smooth cubic Bézier curve from the @ref{current point} to the @emph{end |
| point} specified by @var{x, y}. The @emph{end control point} is specified by |
| @var{x2, y2}. |
| |
| The @emph{start control point} is the reflection of the @emph{end |
| control point} of the previous curve command about the @emph{current |
| point}. |
| |
| The behaviour is identical to the @code{S} command in @svgpathlink{}. For more |
| details, see @mdncubicbeziercurve{}, and the @mdntutorialcurve{}. |
| |
| @vgscmd{s} is like @vgscmd{S}, but the coordinates are relative to the @ref{current |
| point}. |
| |
| @codeexample{ |
| @example |
| M 20 120 |
| |
| c 25 -50, 25 50, 50 0 |
| |
| repeat 3 @{ |
| s 20 50, 50 0 |
| @} |
| |
| stroke |
| @end example |
| } |
| |
| @anchor{cmd_translate} |
| @section @code{translate} |
| |
| @signature{translate @var{tx} @var{ty}} |
| |
| Modifies the current @ref{transformation matrix} by |
| translating the user-space origin by @var{tx, ty}. |
| |
| See the documentation of the |
| @uref{https://www.cairographics.org/manual/cairo-Transformations.html#cairo-translate,@code{cairo_translate}} |
| function for more details. |
| |
| @anchor{cmd_T} |
| @anchor{cmd_t} |
| @section @code{T}, @code{t} |
| |
| @signatureimpl{T, t @var{x} @var{y}} |
| |
| Draw a smooth quadratic Bézier curve from the @ref{current point} to the |
| @emph{end point} specified by @var{x, y}. |
| |
| The @emph{control point} is the reflection of the @emph{control point} |
| of the previous curve command about the @emph{current point}. |
| |
| The behaviour is identical to the @code{T} command in @svgpathlink{}. For more |
| details, see @mdnquadbeziercurve{}, and the @mdntutorialcurve{}. |
| |
| @vgscmd{t} is like @vgscmd{T}, but the coordinates are relative to the |
| @ref{current point}. |
| |
| @codeexample{ |
| @example |
| M 20 120 |
| |
| q 10 -20, 20 0 |
| |
| repeat 9 @{ |
| t 20 0 |
| @} |
| |
| stroke |
| @end example |
| } |
| |
| @anchor{cmd_V} |
| @anchor{cmd_v} |
| @section @code{V}, @code{v} |
| |
| @signatureimpl{V, v @var{y}} |
| |
| Draw a vertical line from the @ref{current point} to y. |
| |
| The coordinate for @vgscmd{V} is absolute, and for @vgscmd{v} it is relative |
| to the @ref{current point}. |
| |
| @bye |