Skip to content

Render Commands

Header: winui/render/Renderer.hpp.

struct FillRoundedRect

Recorded form of Renderer::fill_rounded_rect(): a solid rounded-rect fill.

Example:

// What Renderer::fill_rounded_rect() draws. In an element of your own:
class Panel : public ui::Element
{
    void render_override(ui::Renderer& r, const ui::RenderContext& context) const override
    {
        const ui::Theme& theme = context.theme;
        r.fill_rounded_rect(bounds(), 4.0f, theme.colors().background);
    }
};

Field Type Default
bounds Rect The rect to fill, in DIPs.
radius float 0.0f Corner radius in DIPs; 0 is a plain rect.
color Color The fill color.

struct FillRoundedRectGradient

A gradient fill of a rounded rect (radius 0 == a plain rect), along the brush's GradientAxis: the XP/Vista counterpart to FillRoundedRect's flat color. Recorded form of Renderer::fill_rounded_rect_gradient().

Field Type Default
bounds Rect The rect to fill, in DIPs.
radius float 0.0f Corner radius in DIPs.
brush LinearGradientBrush Stops and axis, mapped across bounds.

struct FillEllipse

A filled ellipse, given by its center and radii. A circle (radius_x == radius_y) given this way has none of the rounding ambiguity of a square rect with a corner radius. The classic circular radio-button bevel is built from these.

Example:

renderer.fill_ellipse(ui::Point{10, 10}, 6, 6, ui::colors::White);
renderer.stroke_ellipse(ui::Point{10, 10}, 6, 6, ui::colors::Black, 1.0f);

Field Type Default
center Point Center, in DIPs.
radius_x float 0.0f Horizontal radius, in DIPs.
radius_y float 0.0f Vertical radius, in DIPs.
color Color The fill color.

struct FillPolygon

A straight-edged polygon filled with one color, such as a menu's cascade arrow. The shape is always closed; StrokePolyline outlines one.

Example:

renderer.fill_polygon(ui::FillPolygon{
    .points = {{0, 10}, {8, 0}, {16, 10}},
    .fill   = ui::colors::White,
});

Field Type Default
points std::vector<Point> Vertices in DIPs, in drawing order.
fill Color Interior color; transparent paints nothing.

struct FillRectPattern

Recorded form of Renderer::fill_rect_pattern(): bounds filled with a Pattern in two colors.

Example:

// The Windows 95 scrollbar trough.
const ui::Rect trough{0, 16, 16, 200};
renderer.fill_rect_pattern(trough, ui::Pattern::Checkerboard50, ui::colors::White,
                           ui::Color::from_rgba(192, 192, 192));

Field Type Default
bounds Rect The rect to fill, in DIPs.
pattern Pattern Pattern::Checkerboard50 Which stipple.
fg Color The "on" pixels.
bg Color The "off" pixels.

struct StrokeRoundedRect

Recorded form of Renderer::stroke_rounded_rect(): a solid outline of a rounded rect, centered on bounds' edge (so half of stroke_width falls outside it).

Field Type Default
bounds Rect The rect the stroke is centered on, in DIPs.
radius float 0.0f Corner radius in DIPs; 0 is a plain rect.
color Color The stroke color.
stroke_width float 1.0f Pen width in DIPs.

struct StrokeRoundedRectGradient

The stroke counterpart of FillRoundedRectGradient, used for Fluent's elevation border: a linear gradient stroked around a rounded rect. The gradient spans extent_px device pixels whatever the height of bounds (Fluent uses 3). A positive span starts at bounds.top() and brush's stops read top to bottom. A negative span starts at bounds.bottom() and the stops read bottom to top, which is how an accent control's flipped border is drawn.

Field Type Default
bounds Rect The rect the stroke is centered on, in DIPs.
radius float 0.0f Corner radius in DIPs.
brush LinearGradientBrush Stops only; the axis is always vertical.
extent_px float 3.0f Signed gradient span in device pixels (see above).
stroke_width float 1.0f Pen width in DIPs.

struct StrokeEllipse

Recorded form of Renderer::stroke_ellipse(): an ellipse outline centered on its edge.

Field Type Default
center Point Center, in DIPs.
radius_x float 0.0f Horizontal radius, in DIPs.
radius_y float 0.0f Vertical radius, in DIPs.
color Color The stroke color.
stroke_width float 1.0f Pen width in DIPs.

struct StrokeLine

A single straight stroked line segment. The classic raised and sunken bevels (a light edge and a dark edge around a flat fill) are built from these: draw_bevel_border() draws four per bevel ring.

Field Type Default
from Point Start point, in DIPs.
to Point End point, in DIPs.
color Color The stroke color.
stroke_width float 1.0f Pen width in DIPs.

struct StrokeWavyLine

A wavy line from from to to, the underline that marks a SpellChecker's misspellings. It is a command of its own, so TestRenderer records one squiggle per word instead of many line segments, and each backend draws its own curve. The wave swings amplitude DIPs to either side of the from→to axis, one full period every wavelength DIPs, starting on the axis.

Field Type Default
from Point Start of the axis, in DIPs.
to Point End of the axis, in DIPs.
color Color The stroke color.
stroke_width float 1.0f Pen width in DIPs.
amplitude float 1.0f Peak distance from the axis, in DIPs.
wavelength float 4.0f Length of one full period along the axis, in DIPs.

struct StrokePolyline

Straight lines through points in order, with the closing back()-to-front() edge only when closed. TeachingTip's tail outlines its two slanted sides this way and leaves open the side it shares with the tip's body.

Example:

const std::vector<ui::Point> tail = {{0, 10}, {8, 0}, {16, 10}};
renderer.fill_polygon(ui::FillPolygon{.points = tail, .fill = ui::colors::White});
renderer.stroke_polyline(ui::StrokePolyline{
    .points       = tail,
    .color        = ui::Color::from_rgba(0, 0, 0, 40),
    .stroke_width = 1.0f,
    .closed       = false, // the two slanted sides only
});

Field Type Default
points std::vector<Point> Vertices in DIPs, in drawing order.
color Color Line color; transparent draws nothing.
stroke_width float 1.0f Line width in DIPs; 0 draws nothing.
closed bool false Draw the closing back()-to-front() edge too.

struct DrawTextRun

Recorded form of Renderer::draw_text(): a UTF-8 string laid out in bounds per style.

Example:

// What Renderer::draw_text() draws. In an element of your own:
class Label : public ui::Element
{
    void render_override(ui::Renderer& r, const ui::RenderContext& context) const override
    {
        const ui::Theme& theme = context.theme;
        r.draw_text("Save", bounds(), theme.colors().text, theme.type.body);
    }
};

Field Type Default
text std::string The UTF-8 text.
bounds Rect Layout box the text is aligned within, in DIPs.
color Color Text color.
style TextStyle Font, size, weight, alignment and wrapping.

struct DrawGeometry

A PathGeometry, filled and then stroked: what every Shape draws. Either brush may be absent. A gradient's relatively mapped points span brush_box and its absolute ones count from its top-left; a Shape passes its own layout rect, since a Shape's brushes map to the element's top-left. An empty brush_box means the geometry's own bounds.

An AcrylicBrush fill depends on drawing order: it blurs whatever was drawn under the geometry before this command, so it must come after the content it sits on. Window::render() draws popups last, so a popup's acrylic blurs the finished tree.

Example:

renderer.draw_geometry(ui::DrawGeometry{
    .geometry     = ui::PathGeometry::ellipse(ui::Point{50, 50}, 40, 40),
    .fill         = ui::Brush{ui::colors::White},
    .stroke       = ui::Brush{ui::Color::from_rgba(0, 120, 215)},
    .stroke_style = {.thickness = 2.0f},
});

Field Type Default
geometry PathGeometry The figures to paint, in DIPs.
fill std::optional<Brush> Interior paint; nullopt skips the fill.
stroke std::optional<Brush> Outline paint; nullopt skips the stroke.
stroke_style StrokeStyle Pen for stroke; ignored when there is none.
brush_box Rect Box gradients map to; empty uses the geometry's bounds.

struct DrawImage

A Bitmap stretched into bounds per stretch, as drawn by Image, Icon and PersonPicture. Renderer::draw_image() takes the Bitmap itself, while DrawImageNineSlice looks an image up by name. The recorded command keeps image_size but not the pixels. A backend reads the pixels during the call.

Example:

const ui::Bitmap photo = ui::Bitmap::read("avatar.jpg", {.max_width = 96, .max_height = 96});
renderer.draw_image(photo, ui::Rect{0, 0, 48, 48}, ui::Stretch::UniformToFill);

Field Type Default
image_size Size Source pixel dimensions, for Stretch's aspect-ratio math.
bounds Rect Destination rect, in DIPs.
stretch Stretch Stretch::Uniform How the image is fitted into bounds.

struct DrawImageRect

A source rectangle of a caller-supplied bitmap (in its pixels) drawn into bounds, scaled on each axis to fill it. The Image element's stretch modes and nine-grid are built from it: cropping (Stretch None or UniformToFill on a smaller element) is a smaller source rect, and a nine-grid is nine of these. Like DrawImage, the recorded command keeps the image's size but not its pixels.

Example:

// Draw the top-left 16x16 tile of a sprite sheet at 2x.
const ui::Bitmap sheet = ui::Bitmap::read_png("sprites.png");
renderer.draw_image_rect(sheet, ui::Rect{0, 0, 16, 16}, ui::Rect{0, 0, 32, 32});

Field Type Default
image_size Size Source pixel dimensions.
source Rect The part of the image drawn, in image pixels.
bounds Rect Destination rect, in DIPs.

struct DrawImageNineSlice

A nine-slice image blit: looks image_name up in whatever ImageThemePack the backend was configured with and draws it stretched (corners verbatim, edges stretched along one axis, center stretched along both) to fill bounds, per slice_margins. libwinui ships no images itself. Without a matching image, or without a pack, which is the common case, the call draws nothing and is no error, so a control can always ask for image chrome.

Example:

renderer.draw_image_nine_slice("button.normal", ui::Rect{0, 0, 75, 23},
                               ui::Thickness{3});

Field Type Default
image_name std::string Key into the backend's ImageThemePack.
bounds Rect Destination rect, in DIPs.
slice_margins Thickness Corner/edge sizes, in the source image's pixels.

struct DrawSvg

An SvgImage drawn into bounds: its viewport is its natural size (or bounds' size when it has none), scaled on each axis to bounds. The recorded command keeps the natural size, not the markup.

Example:

const ui::SvgImage logo = ui::SvgImage::read("logo.svg"); // throws if unreadable
renderer.draw_svg(logo, ui::Rect{0, 0, 64, 64});

Field Type Default
natural_size Size The document's own size in DIPs; empty when it declares none.
bounds Rect Destination rect, in DIPs.

struct PushClip

Restricts subsequent drawing to bounds until the matching PopClip. Pushes and pops must balance within a single render() call. A container that clips, such as Scroll, pushes before rendering its children and pops right after.

Example:

void render_visible(ui::Renderer& renderer, const ui::RenderContext& context, const ui::Rect& viewport,
                    const std::vector<ui::Element*>& visible_children)
{
    renderer.push_clip(viewport);
    for (const ui::Element* child : visible_children)
    {
        child->render(renderer, context);
    }
    renderer.pop_clip();
}

Field Type Default
bounds Rect The clip rect, in DIPs (transformed by any enclosing PushTransform).

struct PushClipEllipse

An elliptical clip, such as PersonPicture's circular photo mask. It ends with the same PopClip as a rect clip. A backend that pops each kind of clip differently tracks which kind it pushed.

Example:

const ui::Bitmap photo = ui::Bitmap::read("avatar.jpg");
renderer.push_clip_ellipse(ui::Point{24, 24}, 24, 24);
renderer.draw_image(photo, ui::Rect{0, 0, 48, 48}, ui::Stretch::UniformToFill);
renderer.pop_clip();

Field Type Default
center Point Center of the ellipse, in DIPs.
radius_x float 0.0f Horizontal radius, in DIPs.
radius_y float 0.0f Vertical radius, in DIPs.

struct PopClip

Ends the innermost PushClip, PushClipEllipse or PushClipRoundedRect.

struct PushTransform

An affine transform of everything drawn until the matching PopTransform: a point p lands at (p.x * scale + p.y * m21 + offset.x, p.x * m12 + p.y * scale_y + offset.y) (a Matrix in render/PathGeometry.hpp's row-vector convention), and nested pushes compose (the inner one applies first). Scroll zooms this way: its content is laid out once at its natural size and drawn magnified. scale_y differs from scale for ScaleToFit's Stretch::Fill. m12 and m21 are non-zero only for a rotation, which comes from Element::set_rotation(). Clips pushed inside a transform are transformed with it, and a rotated clip becomes its bounding box. Pushes and pops balance within one render() call.

Example:

void render_zoomed(ui::Renderer& renderer, const ui::RenderContext& context, const ui::Element& content)
{
    renderer.push_transform(2.0f, ui::Point{10, 10}); // uniform 2x zoom, then offset
    content.render(renderer, context);
    renderer.pop_transform();
}

scale

float scale = 1.0f

Horizontal scale (Matrix::m11); also the vertical one when uniform.

offset

Point offset {}

Translation applied after scaling, in DIPs.

scale_y

float scale_y = 1.0f

Vertical scale (Matrix::m22).

m12

float m12 = 0.0f

Rotation/skew term; 0 unless the element is rotated.

m21

float m21 = 0.0f

Rotation/skew term; 0 unless the element is rotated.

matrix

Matrix matrix() const

The same transform as a Matrix.

struct PopTransform

Ends the innermost PushTransform.

struct PushOpacityLayer

Draws every call between this and the matching PopOpacityLayer as one layer at opacity. Flyouts and dialogs fade in and out this way: Window::render() wraps a flyout's popup, or a dialog's scrim and content together, in one layer at Element::transition_opacity().

At opacity 1, which is every Element that isn't fading, the layer must look like no layer, and a backend may skip it. At 0 or below a backend need not draw the contents at all. Pushes and pops must balance within a single render() call, like PushClip and PopClip, and the two kinds nest inside each other freely.

Example:

void render_faded(ui::Renderer& renderer, const ui::RenderContext& context, const ui::Element& popup_content)
{
    renderer.push_opacity_layer(0.5f);
    popup_content.render(renderer, context); // drawn at half opacity, as one layer
    renderer.pop_opacity_layer();
}

Field Type Default
opacity float 1.0f Layer opacity, 0..1; 1 is a no-op.

struct PopOpacityLayer

Ends the innermost PushOpacityLayer.

struct SetRasterization

The rasterization policy for the whole frame. Window::render() emits it once, at the start, from Theme::rasterization. Because it is a command, TestRenderer records it and describe() prints it, so a test can check that a theme asks for aliased rendering without comparing pixels. The backend applies snap_to_device_pixels while drawing, by rounding each primitive's coordinates to the nearest device pixel, so it stays correct under DPI scaling. Layout is unaffected and stays resolution-independent.

Example:

// A renderer of your own receives it first in every frame and keeps it for what follows.
class ExportRenderer : public ui::Renderer
{
public:
    void set_rasterization(const ui::SetRasterization& policy) override
    {
        antialias_shapes_ = policy.geometry == ui::GeometryAA::Antialiased;
        text_smoothing_   = policy.text;
        snap_to_pixels_   = policy.snap_to_device_pixels;
    }

private:
    bool       antialias_shapes_ = true;
    ui::TextAA text_smoothing_   = ui::TextAA::Grayscale;
    bool       snap_to_pixels_   = false;
};

Field Type Default
geometry GeometryAA GeometryAA::Antialiased Edge policy for shapes and lines.
text TextAA TextAA::Grayscale Text smoothing policy.
snap_to_device_pixels bool false Round primitives to device pixels.

Tests

  • tests/test_render.cpp