Choose a render target

Attach a software pixel buffer, an OpenGL framebuffer, or a WebGPU surface as the dotLottie render target, and control fit, alignment, and viewport.

A player draws nothing until you give it somewhere to draw. The runtime supports three render targets, each enabled by a different build feature, and you attach exactly one per player.

TargetFunctionFeatureUse when
Softwaredotlottie_set_sw_target()tvg-cpuYou want pixels in memory: image export, custom compositing, no GPU
OpenGLdotlottie_set_gl_target()tvg-glYou already have a GL or GLES context and want to render into it
WebGPUdotlottie_set_wg_target()tvg-wgYou have a WebGPU device — typically Metal-backed on Apple platforms

Attaching a target for a feature that wasn't compiled in returns FeatureNotEnabled.

Software rendering

The software target is the simplest and the most portable: you allocate a buffer, and the player rasterizes into it.

uint32_t *buffer = malloc(width * height * sizeof(uint32_t));

dotlottie_set_sw_target(player, buffer, width, height, ABGR8888);

The buffer holds exactly width × height 32-bit pixels with contiguous rows and no stride. You own it, so it must outlive the player — see Memory and safety rules.

Pixel formats

The final argument selects how the eight-bit channels are packed into each 32-bit word, listed from most to least significant byte.

ConstantLayoutAlpha
ABGR88880xAABBGGRRPremultiplied
ARGB88880xAARRGGBBPremultiplied
ABGR8888S0xAABBGGRRNot premultiplied
ARGB8888S0xAARRGGBBNot premultiplied

Pick the one that matches the surface you're copying into: ARGB8888 suits Windows GDI and Cairo, ABGR8888 suits most OpenGL texture uploads and SDL's RGBA32 on little-endian machines.

The S variants give you straight (un-premultiplied) alpha. Choose them when your compositor expects straight alpha, or when you plan to save a transparent PNG — otherwise you would have to divide the color channels by alpha yourself. If you render onto an opaque background the distinction doesn't arise, because alpha is 255 everywhere and the two encodings coincide.

Resizing

To change size, attach a new buffer and only then release the old one:

uint32_t *resized = malloc(new_width * new_height * sizeof(uint32_t));

dotlottie_set_sw_target(player, resized, new_width, new_height, ABGR8888);

free(buffer);      /* safe: the player no longer references it */
buffer = resized;

OpenGL rendering

The GL target renders into a framebuffer object in a context you already own. Because the runtime does not create or manage the context, you must make it current before every call that draws.

dotlottie_set_gl_target(player, display, surface, context, fbo_id, width, height);
ParameterMeaning
displayPlatform display handle — EGLDisplay, HDC. NULL when not applicable
surfacePlatform surface handle — EGLSurface. NULL when not applicable
contextThe GL context to render with
idThe framebuffer object to draw into. 0 targets the default framebuffer
widthTarget width in pixels
heightTarget height in pixels

Pass NULL for display and surface on platforms that have no such handles — macOS CGL, for example, where the context alone is enough. On Android with EGL, all three are required:

dotlottie_set_gl_target(
  player,
  eglGetCurrentDisplay(),
  eglGetCurrentSurface(EGL_DRAW),
  eglGetCurrentContext(),
  0, width, height
);
Make the context current on the rendering thread

The runtime issues GL calls on whichever thread you call dotlottie_render() or dotlottie_tick() from, and it never binds the context for you. If the context isn't current, calls fail silently or corrupt state. Keep all rendering for one player on a single thread with its context bound.

Android builds enable tvg-gl alongside the software renderer by default, so both targets are available without a custom feature list.

WebGPU rendering

The WebGPU target accepts handles from a wgpu-native device and renders either into a surface or into a texture.

dotlottie_set_wg_target(player, device, instance, target, width, height, Surface);

target_type selects what target points at:

ConstantNumerictarget is
Surface0A WGPUSurface to present to
Texture1A WGPUTexture to render into

Use Texture when you want to composite the animation into a larger scene rather than present it directly.

The WebGPU backend is not available on every platform

tvg-wg links prebuilt wgpu-native binaries, which exist only for macOS, iOS and its simulator, Linux, and Android. There is no artifact for any Windows target, nor for macCatalyst, tvOS, visionOS, or watchOS. Use tvg-cpu or tvg-gl there. On Linux, a WebGPU build also needs libvulkan at link time.

Fit and alignment

The animation has its own intrinsic size, which rarely matches your target exactly. dotlottie_set_layout() decides how one maps onto the other.

dotlottieLayout layout;
layout.fit = Contain;
layout.align[0] = 0.5f;   /* horizontal: 0.0 left, 0.5 centre, 1.0 right */
layout.align[1] = 0.5f;   /* vertical:   0.0 top,  0.5 centre, 1.0 bottom */

dotlottie_set_layout(player, layout);
Fit modeBehavior
ContainScale to fit entirely inside the target, preserving aspect ratio
CoverScale to fill the target, preserving aspect ratio and cropping overflow
FillStretch to the target exactly, ignoring aspect ratio
FitWidthMatch the target width; height follows the aspect ratio
FitHeightMatch the target height; width follows the aspect ratio
NoneDraw at the animation's intrinsic size, unscaled

The default is Contain with alignment centred at [0.5, 0.5].

Read the animation's intrinsic size when you need to size a target around it:

float w = 0.0f, h = 0.0f;
dotlottie_get_animation_size(player, &w, &h);

Drawing into part of the target

dotlottie_set_viewport() restricts drawing to a rectangle, which is useful when the animation shares a surface with other content:

dotlottie_set_viewport(player, 20, 20, 200, 200);

Coordinates are in pixels, with the origin at the target's top-left corner. Layout fit and alignment then apply within that rectangle rather than the whole target.

Learn more

Last updated: August 13, 2026 at 9:17 AMEdit this page