valo
Drawing

Layers

Group draws, apply opacity or a filter once, mask with coverage.

save_layer / saveLayer opens an offscreen target. Children draw into it. restore composites that texture as one image using the layer paint's alpha, blend, and filters.

Left: per-draw alpha, so overlaps darken twice. Right: the same shapes inside a layer — the group alpha applies once.

builder.save_layer(None, &Paint::from_color(Color::rgba(0.0, 0.0, 0.0, 0.4)));
builder.draw_circle((200.0, 80.0), 46.0, &opaque);
builder.restore();
builder.saveLayer(new Paint(0, 0, 0, 0.4));
builder.drawCircle(200, 80, 46, opaque);
builder.restore();

Left: per-draw alpha, so overlaps darken twice. Right: the same shapes inside a layer — the group alpha applies once.

scene.jsplayground
loading editor
liveacquiring device

When a layer is skipped

A save-layer whose children are opaque-disjoint and whose paint is only alpha can be elided: children draw in the parent with that alpha on their tint. Depth slots do not change. Backdrop layers never elide — they need a texture to hold the blur.

saveLayerBounds / a bounds hint crops children. Hinted layers also skip elision.

Mask layers

save_layer_mask composites as coverage (DstIn over the parent), not as a picture. MaskKind::Luminance uses luma×alpha; MaskKind::Alpha uses alpha only. A mask whose bounds miss the cull rect still erases the parent — coverage is zero everywhere.

Rust:

builder.save_layer_mask(None, MaskKind::Luminance);
builder.draw_rect(rect, &Paint::from_color(Color::WHITE));
builder.restore();

The JavaScript engine API does not yet expose mask layers; use Rust or wait for a later valo-web binding.

On this page