Getting Started

Blend2D is a relatively small project that has only one dependency (AsmJit), which is usually built together with Blend2D itself. It's recommended to visit a download page for download and build instructions although the instructions provided here are complete and show how to build a first application with a statically linked Blend2D. The instructions shown here should work for all operating systems and all IDEs and build systems that are supported by CMake.

All samples shown on this page are part of blend2d-samples package available on GitHub. This means that you don't have to go over all the preparation steps if you prefer cloning the samples from GitHub or downloading them as a part of "all" package.

Preparation Steps

We are going to start from scratch so we will create a new directory and call it a workspace. You can chose any other name, but in this document we will always refer to the workspace:

# Create the workspace and move there.
$ mkdir workspace
$ cd workspace

We need both AsmJit and Blend2D libraries so let's clone them into the workspace we just created:

# Get both Blend2D and AsmJit.
$ git clone --depth=1 https://github.com/asmjit/asmjit
$ git clone --depth=1 https://github.com/blend2d/blend2d

First Application

Our workspace is ready, now we are going to create the first application, let's call it app:

# Create 'app' directory for the application and move there.
$ mkdir app
$ cd app

# Create a CMakeLists.txt project file.
$ touch CMakeLists.txt

# Create the source file of the application.
$ touch app.cpp

# Create a build directory for the application. This highly depends on
# your build system. If you use IDE you would need just one directory,
# if you use other build system like make or ninja your may need more
# directories for debug and release builds. For simplicity we will use
# only one directory called 'build'.
$ mkdir build

You should end up with the following directory structure If everything went right:

  • workspace
    • app
      • build
      • app.cpp
      • CMakeLists.txt
    • asmjit
    • blend2d

We will start with the following cmake project file CMakeLists.txt:

cmake_minimum_required(VERSION 3.8)

project(app CXX)

# Some basics, set accordingly to your needs.
set(CMAKE_CXX_STANDARD 11)

# Where the 'app' and 'blend2d' are.
set(APP_DIR "${CMAKE_CURRENT_LIST_DIR}"
    CACHE PATH "Application directory")

set(BLEND2D_DIR "${APP_DIR}/../blend2d"
    CACHE PATH "Location of 'blend2d'")

# Enable Blend2D static build.
set(BLEND2D_STATIC TRUE)
include("${BLEND2D_DIR}/CMakeLists.txt")

# Your application target.
add_executable(app app.cpp)

# Add Blend2D dependency to your application. The dependency
# should provide everything needed - include paths, libraries,
# compiler flags, and compiler definitions.
target_link_libraries(app Blend2D::Blend2D)

And finally put some C++ code to app.cpp:

C++ Source

#include <blend2d.h>

int main(int argc, char* argv[]) {
  BLImage img(480, 480, BL_FORMAT_PRGB32);

  // Attach a rendering context into `img`.
  BLContext ctx(img);

  // Clear the image.
  ctx.setCompOp(BL_COMP_OP_SRC_COPY);
  ctx.fillAll();

  // Fill some path.
  BLPath path;
  path.moveTo(26, 31);
  path.cubicTo(642, 132, 587, -136, 25, 464);
  path.cubicTo(882, 404, 144, 267, 27, 31);

  ctx.setCompOp(BL_COMP_OP_SRC_OVER);
  ctx.setFillStyle(BLRgba32(0xFFFFFFFF));
  ctx.fillPath(path);

  // Detach the rendering context from `img`.
  ctx.end();

  // Let's use some built-in codecs provided by Blend2D.
  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("bl-getting-started-1.bmp", codec);

  return 0;
}

Output

If you compile and run the application it should create the image shown above.

As can be seen in the example Blend2D C++ API uses a BL prefix and uses RAII for all classes that require memory management. This design simplifies the use of the API and minimizes the possibility of memory leaks. In addition, Blend2D API is exception-safe and would never throw an exception, handling errors will be described later.

Gradients

Blend2D provides a BLGradient class that can be used to describe the following gradients:

  • Linear gradient from [x0, y0] to [x1, y1].
  • Radial gradient having the center at [cx, cy], focal point at [fx, fy], and radius r.
  • Conical gradient having the center at [cx, cy] and angle a.

The next example shows how to use create a linear gradient:

C++ Source

#include <blend2d.h>

int main(int argc, char* argv[]) {
  BLImage img(480, 480, BL_FORMAT_PRGB32);
  BLContext ctx(img);

  ctx.setCompOp(BL_COMP_OP_SRC_COPY);
  ctx.fillAll();

  // Coordinates can be specified now or changed later.
  BLGradient linear(BLLinearGradientValues(0, 0, 0, 480));

  // Color stops can be added in any order.
  linear.addStop(0.0, BLRgba32(0xFFFFFFFF));
  linear.addStop(0.5, BLRgba32(0xFF5FAFDF));
  linear.addStop(1.0, BLRgba32(0xFF2F5FDF));

  // `setFillStyle()` can be used for both colors and styles.
  ctx.setFillStyle(linear);

  ctx.setCompOp(BL_COMP_OP_SRC_OVER);
  ctx.fillRoundRect(40.0, 40.0, 400.0, 400.0, 45.5);
  ctx.end();

  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("bl-getting-started-2.bmp", codec);

  return 0;
}

Output

To make this document shorter we will not demonstrade radial and conical gradients as they will be used in later examples. In addition, the example above used fractional coordinates when calling fillRound() to show that Blend2D API is not restricted to integers. All function accept double precision floating point coordinates with few exceptions where integral coordinates are allowed like fillRect().

Patterns

Pattern in Blend2D represents an image, transformation matrix, and other properties like extend mode and filter. A single image can be used by multiple patterns and it's also possible to use only a part of an image as a pattern.

C++ Source

#include <blend2d.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
  BLImage img(480, 480, BL_FORMAT_PRGB32);
  BLContext ctx(img);

  ctx.setCompOp(BL_COMP_OP_SRC_COPY);
  ctx.fillAll();

  // Read an image from file.
  BLImage texture;
  BLResult err = texture.readFromFile("texture.jpeg");

  // Basic error handling is necessary as we need some IO.
  if (err) {
    printf("Failed to load a texture (err=%u)\n", err);
    return 1;
  }

  // Create a pattern and use it to fill a rounded-rect.
  BLPattern pattern(texture);

  ctx.setCompOp(BL_COMP_OP_SRC_OVER);
  ctx.setFillStyle(pattern);
  ctx.fillRoundRect(40.0, 40.0, 400.0, 400.0, 45.5);

  ctx.end();

  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("bl-getting-started-3.bmp", codec);

  return 0;
}

Output

Transformations

Transformations can be applied to geometry, images, and styles.

C++ Source

#include <blend2d.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
  BLImage img(480, 480, BL_FORMAT_PRGB32);
  BLContext ctx(img);

  ctx.setCompOp(BL_COMP_OP_SRC_COPY);
  ctx.fillAll();

  // Read an image from file.
  BLImage texture;
  BLResult err = texture.readFromFile("texture.jpeg");

  if (err) {
    printf("Failed to load a texture (err=%u)\n", err);
    return 1;
  }

  // Rotate by 45 degrees about a point at [240, 240].
  ctx.rotate(0.785398, 240.0, 240.0);

  // Create a pattern.
  ctx.setCompOp(BL_COMP_OP_SRC_OVER);
  ctx.setFillStyle(BLPattern(texture));
  ctx.fillRoundRect(50.0, 50.0, 380.0, 380.0, 80.5);

  ctx.end();

  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("bl-getting-started-4.bmp", codec);

  return 0;
}

Output

The exactly same transformations that can be applied to BLContext can also be applied to paths and styles. It's also possible to use a BLMatrix2D to build a transformation matrix consisting of several consecutive transformations and apply it at once.

Composition

Blend2D supports all Porter & Duff composition operators and many blending operators that are described in SVG and PDF specifications. These operators can be set via BLContext::setCompOp() function.

C++ Source

#include <blend2d.h>

int main(int argc, char* argv[]) {
  BLImage img(480, 480, BL_FORMAT_PRGB32);
  BLContext ctx(img);

  ctx.setCompOp(BL_COMP_OP_SRC_COPY);
  ctx.fillAll();

  // First shape filld by a radial gradient.
  BLGradient radial(
    BLRadialGradientValues(180, 180, 180, 180, 180));
  radial.addStop(0.0, BLRgba32(0xFFFFFFFF));
  radial.addStop(1.0, BLRgba32(0xFFFF6F3F));

  ctx.setCompOp(BL_COMP_OP_SRC_OVER);
  ctx.setFillStyle(radial);
  ctx.fillCircle(180, 180, 160);

  // Second shape filled by a linear gradient.
  BLGradient linear(
    BLLinearGradientValues(195, 195, 470, 470));
  linear.addStop(0.0, BLRgba32(0xFFFFFFFF));
  linear.addStop(1.0, BLRgba32(0xFF3F9FFF));

  ctx.setCompOp(BL_COMP_OP_DIFFERENCE);
  ctx.setFillStyle(linear);
  ctx.fillRoundRect(195, 195, 270, 270, 25);

  ctx.end();

  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("bl-getting-started-5.bmp", codec);

  return 0;
}

Output

Stroking

Stroking can be specified through BLStrokeOptions class or set directly through BLContext API as demonstrated in the example below.

C++ Source

#include <blend2d.h>

int main(int argc, char* argv[]) {
  BLImage img(480, 480, BL_FORMAT_PRGB32);
  BLContext ctx(img);

  ctx.setCompOp(BL_COMP_OP_SRC_COPY);
  ctx.fillAll();

  BLGradient linear(BLLinearGradientValues(0, 0, 0, 480));
  linear.addStop(0.0, BLRgba32(0xFFFFFFFF));
  linear.addStop(1.0, BLRgba32(0xFF1F7FFF));

  BLPath path;
  path.moveTo(119, 49);
  path.cubicTo(259, 29, 99, 279, 275, 267);
  path.cubicTo(537, 245, 300, -170, 274, 430);

  ctx.setCompOp(BL_COMP_OP_SRC_OVER);
  ctx.setStrokeStyle(linear);
  ctx.setStrokeWidth(15);
  ctx.setStrokeStartCap(BL_STROKE_CAP_ROUND);
  ctx.setStrokeEndCap(BL_STROKE_CAP_BUTT);
  ctx.strokePath(path);

  ctx.end();

  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("bl-getting-started-6.bmp", codec);

  return 0;
}

Output

Text Rendering

Text rendering was added recently to Blend2D and it's one of the features that would need further improvements. Blend2D provides both low-level and high-level interfaces to render text and the following example shows how to use the high-level one:

C++ Source

#include <blend2d.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
  BLImage img(480, 480, BL_FORMAT_PRGB32);
  BLContext ctx(img);

  ctx.setCompOp(BL_COMP_OP_SRC_COPY);
  ctx.fillAll();

  BLFontFace face;
  BLResult err = face.createFromFile("NotoSans-Regular.ttf");

  // We must handle a possible error returned by the loader.
  if (err) {
    printf("Failed to load a font-face (err=%u)\n", err);
    return 1;
  }

  BLFont font;
  font.createFromFace(face, 50.0f);

  ctx.setFillStyle(BLRgba32(0xFFFFFFFF));
  ctx.fillUtf8Text(BLPoint(60, 80), font, "Hello Blend2D!");

  ctx.rotate(0.785398);
  ctx.fillUtf8Text(BLPoint(250, 80), font, "Rotated Text");

  ctx.end();

  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("bl-getting-started-7.bmp", codec);

  return 0;
}

Output

Glyph Buffer

BLGlyphBuffer is a low-level interface that can be used to convert text to glyphs and to retrieve useful information about them. If you need to position text or calculate text metrics then BLGlyphBuffer is the only way of obtaining such information.

C++ Source

#include <blend2d.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
  BLImage img(480, 480, BL_FORMAT_PRGB32);
  BLContext ctx(img);

  ctx.setCompOp(BL_COMP_OP_SRC_COPY);
  ctx.fillAll();
  ctx.setFillStyle(BLRgba32(0xFFFFFFFF));

  BLFontFace face;
  BLResult err = face.createFromFile("NotoSans-Regular.ttf");
  if (err) {
    printf("Failed to load a font-face (err=%u)\n", err);
    return 1;
  }

  BLFont font;
  font.createFromFace(face, 20.0f);

  BLFontMetrics fm = font.metrics();
  BLTextMetrics tm;
  BLGlyphBuffer gb;

  BLPoint p(20, 190 + fm.ascent);
  const char* text = "Hello Blend2D!\n"
                     "I'm a simple multiline text example\n"
                     "that uses BLGlyphBuffer and fillGlyphRun!";
  for (;;) {
    const char* end = strchr(text, '\n');
    gb.setUtf8Text(text, end ? (size_t)(end - text) : SIZE_MAX);
    font.shape(gb);
    font.getTextMetrics(gb, tm);

    p.x = (480.0 - (tm.boundingBox.x1 - tm.boundingBox.x0)) / 2.0;
    ctx.fillGlyphRun(p, font, gb.glyphRun());
    p.y += fm.ascent + fm.descent + fm.lineGap;

    if (!end) break;
    text = end + 1;
  }
  ctx.end();

  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("bl-getting-started-8.bmp", codec);

  return 0;
}

Output

More...?

In addition to samples mentioned above blend2d-samples package also provides examples that use Qt library. These samples provide either interactive demonstrations of some Blend2D features like stroking or animated demonstrations that can use both Blend2D and Qt rendering engine for both performance and quality comparison.