top of page

Tutorial

It explains how to set up the environment for building ThorVG and how to draw various graphic elements through ThorVG with simple examples.

Build and install

You can download the ThorVG tarball via the Releases link.  The latest version is recommended.

ThorVG supports meson build system. Install the meson and ninja if you don't have them already.

Run meson to configure ThorVG in the thorvg root folder.

meson setup builddir

Run ninja to build and install ThorVG:

ninja -C builddir install

Regardless of the installation, all build results (symbols, executable) are generated in the builddir folder in thorvg. Some results such as examples won't be installed, you can check More examples section to see how to change it.

Note that some systems might include ThorVG package as a default component. In that case, you can skip this manual installation.

Build with Visual Studio

If you want to create Visual Studio project files, use the command --backend=vs. The resulting solution file (thorvg.sln) will be located in the build folder.

meson setup builddir --backend=vs

Install with vcpkg

You can easily download and install the pre-packaged ThorVG using the vcpkg package manager. First, ensure you are in the desired directory where you want to install the tool. Then, clone the vcpkg repository with the following command:

Run ninja to build and install ThorVG:

./bootstrap-vcpkg.sh

Install the ThorVG package.

./vcpkg install thorvg

Install with Conan

You can download and install the pre-packaged ThorVG using the Conan package manager. Start by following the instructions on this page to set up Conan on your system. Once Conan is configured, proceed to install the ThorVG package by executing the appropriate command in your command line interface:

conan install --requires="thorvg/[*]" --build=missing

Install with MSYS2

To install ThorVG using the MSYS2 package manager, begin by downloading and running the MSYS2 installer from their official website. Follow the on-screen instructions to complete the installation. Once installed, open one of the provided terminals from the Start Menu, selecting the appropriate one based on your system's architecture (either 32-bit or 64-bit) and the desired compiler library (MSVCRT or UCRT). After setting up the terminal, you can install the ThorVG package by executing the following command in the terminal:

pacman -S thorvg

Basic Programming

ThorVG supports C++ Programming Interfaces. The following is a quick-start to show you how to use the essential APIs. As the prerequisite, include the ThorVG header file in your source code.

#include <thorvg.h>

Initialization

In the first step, initialize the ThorVG engine. This prepares and runs the engine internal steps.

// Initialize the ThorVG engine
tvg::Initializer::init(0);

The second parameter of the Initializer::init() requires the ThorVG designated threads number. To use full capacity of the system, you can pass the number of threads to run ThorVG tasks. It will spawn the threads and run Task-Scheduler to progress multiple jobs asynchronously. If you don't know the exact number, you can use std::thread::hardware_concurrency() instead.

ThorVG renders vector scenes to the given canvas buffer. The following shows you an example how to prepare an empty canvas for drawing: 

// A canvas target buffer
static uint32_t buffer[WIDTH * HEIGHT];
// Generate a canvas
auto canvas = tvg::SwCanvas::gen();
// Setup the canvas target
canvas->target(buffer, WIDTH, WIDTH, HEIGHT, tvg::SwCanvas::ARGB8888);

If you have your own canvas buffer memory, you can pass its pointer to the canvas. SwCanvas::target() requires five parameters: buffer memory, buffer stride size, canvas width, height and Colorspace. The last parameter determines the format of the pixel color channels used during drawing scene on the canvas buffer.

Shape

Once a canvas is ready, you can create shapes by pushing them to the canvas.

// Generate a shape
auto rect = tvg::Shape::gen();
// Append a rounded rectangle to the shape (x, y, w, h, rx, ry)
rect->appendRect(50, 50, 200, 200, 20, 20);
// Set the s
hape's color to (r, g, b)
rect->fill(100, 100, 100);
// Push the shape into the canvas
canvas->push(move(rect));

In the example above, a shape is generated and then a rounded rectangle is appended to it. ThorVG provides predefined shape types such as rectangle, circle and arc for the user's convenience. You can append any custom shape by using Paths. ThorVG allows you to append multiple forms into a shape, to compose a more complex one. A complex shape shares its properties, such as color, stroke, fill, etc., among the appended forms.

​After a rounded rectangle is appended, its color is set and then the shape is pushed to the canvas.

​This shape from the example looks as follows:

Path

Besides predefined shape types, you can compose arbitrary shape types using a path concept. A path is a list of commands that are commonly used in traditional 2D vector drawing. Below you can see an example of how to define your own forms.

// Generate a shape
auto path = tvg::Shape::gen();        
// Set the sequential path coordinates
path->moveTo(199, 34);
path->lineTo(253, 143);
path->lineTo(374, 160);
path->lineTo(287, 244);
path->lineTo(307, 365);
path->lineTo(199, 309);
path->lineTo(97, 365);
path->lineTo(112, 245);
path->lineTo(26, 161);
path->lineTo(146, 143);
// Complete the path
path->close();
// Set the s
hape's color to (r, g, b)
path->fill(150, 150, 255);
// Push the shape into the canvas
canvas->push(move(path));

By using the Path, lines and Bezier curves can be drawn. Additionally, you can set preset list using 
Shape:appendPath() for optimal data delivery.

The output of the example is as follows:

example_path.webp
Fill

Besides predefined shape types, you can compose arbitrary shape types using a path concept. A path is a list of commands that are commonly used in traditional 2D vector drawing. Below you can see an example of how to define your own forms.

// Generate a shape
auto circle = tvg::Shape::gen();
// Append a circle to the shape (cx, cy, rx, ry)
circle->appendCircle(400, 400, 100, 100);

// Generate a radial gradient
auto fill = tvg::RadialGradient::gen();
// Set the radial gradient geometry info (cx, cy, radius)
fill->radial(400, 400, 150);

// Gradient colors
tvg::Fill::ColorStop colorStops[2];
// 1st color values (offset, r, g, b, a)
colorStops[0] = {0.0, 255, 255, 255, 255};
// 2nd color values (offset, r, g, b, a)
colorStops[1] = {1.0, 0, 0, 0, 255};
// Set the gradient colors info
fill->colorStops(colorStops, 2);

// Set the shape fill
circle->fill(move(fill));

// Push the shape into the canvas
canvas->push(move(circle));

The output of the example is as follows:

example_fill.webp
Stroke

Stroking enables you to draw the outline of shapes as well lines. You can simply add stroke properties to a shape if needed. Stroke supports both a solid color and a gradient fill and also 4 major properties - width, cap, join and dash pattern.

// Generate a shape
auto rect = tvg::Shape::gen();
// Append a round rectangle to the shape (x, y, w, h, rx, ry)
rect->appendRect(50, 50, 200, 200, 20, 20);
// Set the s
hape's color to (r, g, b)
rect->fill(100, 100, 100);
 
// Set the stroke's width
rect->strokeWidth(4);
// Set the stroke's color to (r, g, b)
rect->strokeFill(50, 50, 50);
// Set the stroke's join style
rect->strokeJoin(tvg::StrokeJoin::Round);
// Set the stroke's cap style
rect->strokeCap(tvg::StrokeCap::Round);
 
// Set the stroke's dash pattern (line, gap)
float pattern[2] = {7, 10};
​// Set the stroke pattern
rect->strokeDash(pattern, 2);

// Push the shape into the canvas
canvas->push(move(rect));

The output of the example is as follows:

example_stroking.webp
Scene and Transformation

ThorVG provides an interface to build Paint groups by composing multiple Paints. This is useful when you consider a scene-graph structure and manipulate a scene as a control unit. The code below shows how to use the ThorVG Scene and transform it.

// Generate a Scene
auto scene = tvg::Scene::gen();

// Generate a round rectangle
auto rect = tvg::Shape::gen();
rect->appendRect(-235, -250, 400, 400, 50, 50);
rect->fill(0, 255, 0);
// Push the rectangle into the scene
scene->push(move(rect));

// Generate a circle
auto circle = tvg::Shape::gen();
circle->appendCircle(-165, -150, 200, 200);
// Set the shape's color to (r, g, b, a)

circle->fill(255, 255, 0, 127);
// Push the circle into the scene
scene->push(move(circle));

// Generate an ellipse
auto ellipse = tvg::Shape::gen();
ellipse->appendCircle(265, 250, 150, 100);
ellipse->fill(0, 255, 255);
// Push the eliipse into the scene
scene->push(move(ellipse));

// Transform the scene
scene->translate(350, 350);
scene->scale(0.5);
scene->rotate(45);

// Push the scene into the canvas
canvas->push(move(scene));

All kinds of Paint type nodes (Shape, Scene and Picture) can be pushed into the Scene as its children. You can scale this logic and build a complex scene by compositing multiple Scenes. In the example, we create a scene-graph tree and we demonstrate how to transform it using translate(), scale(), rotate() methods. ThorVG also supports the transform() method which expects a transformation matrix.

The output of the example is as follows:

example_scene.webp
Picture

The Picture is a special component that is designed to draw a scene on the Canvas from image data. ThorVG supports several image formats including vector-based and bitmap-based formats.

// Generate a picture (svg)
auto svg = tvg::Picture::gen();
// Load a svg file
svg->load("logo.svg");
// Push the picture into the canvas
canvas->push(move(svg));

// Generate a picture (png)
auto png = tvg::Picture::gen();
// Load a png file
png->load("lenna.png");
// Set the desired size
png->size(300, 300);
// Set the position
png->translate(150, 150);
// Push the picture into the canvas
canvas->push(move(png));

The output of the example is as follows:

example_picture.webp
Text

ThorVG offers robust text rendering capabilities, including support for Scalable TrueType Fonts (TTF). It efficiently handles Unicode text, accepting UTF-8(or ASCII) characters as input. ThorVG processes these characters by converting them into Unicode codepoints, subsequently generating vector data for the corresponding glyphs. Additionally, ThorVG utilizes the metrics of TTF glyphs to create horizontal layouts, further enhancing its text rendering functionality.

// Load a global font data
Text::load(EXAMPLE_DIR"/arial.ttf");

// Generate a solid color text for displaying

auto text = tvg::Text::gen();

// Set the font name and font size

text->font("Arial", 50);

// Set the text to display

text->text("ThorVG Solid Text");

// Set the text color(r, g, b)

text->fill(0, 0, 0);

// Set the position

text->translate(150, 150);

// Push the text into the canvas

canvas->push(move(text));

// Generate a linear gradient color text for displaying

auto text2 = tvg::Text::gen();

// Set the font name and font size

text2->font("Arial", 50);

// Set the text to display

text2->text("ThorVG Gradient Text");

// Get the text geometry

float x, y, w, h;

text2->bounds(&x, &y, &w, &h);

// Generate a linear gradient

auto fill = tvg::LinearGradient::gen();

// Set the linear gradient geometry info (x1, y1, x2, y2)

fill->linear(x, y + h * 0.5f, x + w, y + h * 0.5f);

// Gradient colors
tvg::Fill::ColorStop colorStops[2];
// 1st color values (offset, r, g, b, a)
colorStops[0] = {0.0, 255, 0, 0, 0};
// 2nd color values (offset, r, g, b, a)
colorStops[1] = {1.0, 255, 255, 0, 255};
// Set the gradient colors info
fill->colorStops(colorStops, 2);

// Set the text fill
text2->fill(move(fill));

// Set the position

text2->translate(150, 250);

// Push the text into the canvas

canvas->push(move(text2));

Note that the global font data loaded into ThorVG is typically shared among various text objects. This font data is automatically released when the ThorVG engine is terminated, which is done using Initializer::term(). If you need to immediately free up the font resources, You can manually unload the font data using the Text::unload() API.

The output of the example is as follows:

example_text.png
Composition

ThorVG applies composition for visual effects such as blending, masking, filtering, etc. You should be aware though, that a composition may perform an additional render-processing on an off-screen buffer. The excessive usage of a composition won't be helpful if lightweight processing is a priority for you. A hint - sometimes you can avoid a composition by changing the application or the design approach while maintaining the same visual effects.

// Generate a picture
auto picture = tvg::Picture::gen();
picture->load(EXAMPLE_DIR"/logo.svg");
// Generate a circle for masking
auto mask = tvg::Shape::gen();
mask->appendCircle(250, 325, 225, 225);
mask->fill(255, 255, 255, 255);

// Set the circle's alpha mask to the picture
picture->composite(move(mask), tvg::CompositeMethod::AlphaMask);
// Push the picture into the canvas
canvas->push(move(picture));

The output of the example is as follows:

example_masking.webp
Animation

The Animation component facilitates the manipulation of animatable elements, such as Lottie. It enables the display and fundamental control of animated frames. Essentially, one animation instance corresponds to a single picture instance. You can assign any animatable resources to the associated picture and play the animation using the animation instance.

// Generate an animation
auto animation = tvg::Animation::gen();
// Acquire a picture which associated with the animation
auto picture = animation->picture();

// Load an animation file.
picture->load("lottie.json");
// Figure out the animation duration time in seconds
auto duration = animation->duration();

// Push the picture into the canvas
canvas->push(tvg::cast(picture));

First, an animation and a picture are generated. The Lottie file (lottie.json) is loaded into the picture, and then the picture is added to the canvas. The animation frames are controlled using the animation object to play the Lottie animation. Also you might want to know the animation duration time to run your animation loop.

// Set a current animation frame to display
animation->frame(animation->totalFrame() * progress);
// Update the picture to be redrawn
canvas->update(animation->picture());

Let's suppose the progress variable determines the position of the animation, ranging from 0 to 1 based on the total duration time of the animation. Adjusting the progress value allows you to control the animation at the desired position. Afterwards, the canvas is updated to redraw the picture with the updated animation frame.

b144cf_85b677ebb075416c96d81e92ed8374bc~mv2.gif
Drawing, the final step

Once you've prepared all the paint nodes by pushing them into the canvas, you can request the drawing task as the final step. The Canvas::push() API initiates the preprocessing task for the rendering, but it doesn't trigger rasterization. As all the tasks are performed asynchronously under the hood, you must call Canvas::sync() at the optimal time to ensure synchronization. Finally, you will receive a completed image in the target buffer.

canvas->draw();
canvas->sync();

After getting the frame image, you can flush out all the pushed paint nodes from the canvas with Canvas::clear() call.

More examples

There are plenty of sample code in thorvg/examples to help you in understanding the ThorVG APIs.

To execute these examples, you can build them with the following meson build option:

meson setup builddir -Dexamples=true

Note that these examples require the SDL dev package for launching. If you're using Linux-based OS, you can easily install this package from your OS distribution server. For Ubuntu, you can install it with this command.

apt-get install libsdl2-dev

Alternatively, you can read the official guidance here for other platforms. Fore more information, please visit the official SDL site.

bottom of page