-
Notifications
You must be signed in to change notification settings - Fork 1
API Rendering
Several SDK contracts hand the plugin an IRenderCanvas instead of asking it for
raw bytes: side strips, strip segments, and
IDisplayImageCommand. The canvas is the
host's own drawing surface — when you draw text or a symbol on it, the host
renders it with the same font and symbol library it uses everywhere else, so
plugin output stays visually consistent with the core. For anything the
primitives don't cover, DrawImage blits bytes you rasterized
yourself.
This replaces the older "return a PNG" model: render methods now draw onto a
canvas and return bool — true when you drew, false to let the host fall
back to its default rendering for that region.
The canvas is valid only during the synchronous render call that received it. Do not cache it or use it after the method returns. Coordinates are canvas-local:
(0,0)is the top-left of this region andWidth×Heightis the region size in device pixels (e.g. 60×270 for a whole Razer strip, 60×90 for one segment, 90×90 for a touch button).
public interface IRenderCanvas
{
int Width { get; }
int Height { get; }
void Clear(PluginColor color);
// Rectangles
void FillRectangle(int x, int y, int width, int height, PluginColor color);
void DrawRectangle(int x, int y, int width, int height, int strokeWidth, PluginColor color);
void FillRoundedRectangle(int x, int y, int width, int height, int radius, PluginColor color);
void DrawRoundedRectangle(int x, int y, int width, int height, int radius, int strokeWidth, PluginColor color);
// Circles / ellipses / arcs
void FillCircle(int centerX, int centerY, int radius, PluginColor color);
void DrawCircle(int centerX, int centerY, int radius, int strokeWidth, PluginColor color);
void FillEllipse(int x, int y, int width, int height, PluginColor color);
void DrawEllipse(int x, int y, int width, int height, int strokeWidth, PluginColor color);
void DrawArc(int x, int y, int width, int height, float startAngle, float sweepAngle, int strokeWidth, PluginColor color);
void FillArc(int x, int y, int width, int height, float startAngle, float sweepAngle, PluginColor color);
// Lines
void DrawLine(int x1, int y1, int x2, int y2, int strokeWidth, PluginColor color);
// Text
void DrawText(string text, int x, int y, int width, int height, PluginColor color,
float fontSize, bool bold = false, bool italic = false,
bool centered = true, bool outlined = false, PluginColor outlineColor = default);
void DrawText(string text, int x, int y, int width, int height, PluginColor color,
float fontSize, TextHAlign hAlign, TextVAlign vAlign,
bool bold = false, bool italic = false, bool outlined = false, PluginColor outlineColor = default);
float MeasureText(string text, float fontSize, bool bold = false, bool italic = false);
// Symbols
void DrawSymbol(string symbolId, int x, int y, int width, int height, PluginColor tint);
void DrawSymbol(string symbolId, int x, int y, int width, int height, SymbolStyle style);
// Images
void DrawImage(byte[] imageBytes, int x, int y, int width, int height);
void DrawImage(byte[] imageBytes, int x, int y, int width, int height, byte opacity, PluginColor tint = default);
// Transform
void PushTransform();
void PopTransform();
void Translate(float dx, float dy);
void Rotate(float degrees);
void Scale(float sx, float sy);
}All shape primitives take integer device-pixel coordinates and a
PluginColor. Each has a Fill… (solid) and a Draw…
(stroke, with an explicit strokeWidth) form.
| Group | Members |
|---|---|
| Rectangles |
FillRectangle / DrawRectangle, FillRoundedRectangle / DrawRoundedRectangle (corner radius) |
| Circles & ellipses |
FillCircle / DrawCircle (center + radius), FillEllipse / DrawEllipse (bounding box) |
| Arcs |
DrawArc (stroked) / FillArc (pie wedge) — angles in degrees, clockwise, 0° at 3 o'clock. Good for round gauges. |
| Lines | DrawLine |
Clear(color) fills the whole region with one color — it does not touch
pixels outside this canvas's region, so clearing a segment never wipes the rest
of the strip.
canvas.DrawText("75 %", 0, 0, canvas.Width, canvas.Height,
PluginColor.White, fontSize: 14f, centered: true);Both DrawText overloads word-wrap inside the box at (x, y, width, height) in
the host's UI font. The first overload toggles centered-vs-top-left with the
centered flag; the second takes independent TextHAlign / TextVAlign. Pass
outlined: true (with an outlineColor) to stroke an outline behind the fill —
the standard trick for keeping a label legible over an arbitrary page wallpaper.
MeasureText returns the single-line advance width (no wrapping) so you can
fit or ellipsize text before drawing it — essential on the narrow 60 px strip:
static string Fit(string text, IRenderCanvas canvas, float size, float maxWidth)
{
if (canvas.MeasureText(text, size) <= maxWidth) return text;
var t = text;
while (t.Length > 1 && canvas.MeasureText(t + "…", size) > maxWidth) t = t[..^1];
return t + "…";
}DrawSymbol(id, x, y, w, h, tint) draws a glyph from the host's symbol library,
tinted and fitted into the box; an unknown id draws a placeholder. The
SymbolStyle overload adds outline, drop shadow, linear gradient fill and
rotation — see SymbolStyle.
DrawImage(bytes, x, y, w, h) decodes bytes (PNG or any host-decodable
format) and blits it aspect-fit into the box. This is the escape hatch for
fully custom, plugin-rasterized content. The opacity/tint overload multiplies an
alpha (0–255) and an optional tint over the image.
Decode caching. Repeated calls with the same
byte[]instance reuse a cached decode, so a plugin holding a static image does not pay the decode cost on every frame. Keep your image bytes in a field and pass that same array each frame — don't re-allocate it per call.
PushTransform / PopTransform save and restore the current transform;
Translate, Rotate (degrees, clockwise) and Scale compose onto it. Rotation
and scaling are about the current origin — Translate to your pivot first to
rotate about a point:
canvas.PushTransform();
canvas.Translate(cx, cy);
canvas.Rotate(angle);
canvas.DrawText(label, -w / 2, -h / 2, w, h, color, size);
canvas.PopTransform();public readonly record struct PluginColor(byte R, byte G, byte B, byte A = 255)
{
public static PluginColor Black => new(0, 0, 0);
public static PluginColor White => new(255, 255, 255);
public static PluginColor Transparent => new(0, 0, 0, 0);
public static PluginColor FromRgb(byte r, byte g, byte b) => new(r, g, b);
}A plain RGBA color. The SDK is UI-framework agnostic, so it exposes neither
Avalonia nor SkiaSharp color types — the host converts as needed. Define your
palette as static readonly fields and reuse them:
private static readonly PluginColor Track = new(48, 48, 48);
private static readonly PluginColor FillActive = new(0x4C, 0xAF, 0x50); // greenpublic readonly record struct SymbolStyle
{
public SymbolStyle(PluginColor tint);
public PluginColor Tint { get; init; } = PluginColor.White; // ignored when UseGradient
public float Rotation { get; init; } // degrees, clockwise
public bool Outlined { get; init; }
public PluginColor OutlineColor { get; init; }
public float OutlineWidth { get; init; }
public bool Shadow { get; init; }
public PluginColor ShadowColor { get; init; }
public float ShadowBlur { get; init; }
public int ShadowOffsetX { get; init; }
public int ShadowOffsetY { get; init; }
public bool UseGradient { get; init; }
public PluginColor GradientStart { get; init; }
public PluginColor GradientEnd { get; init; }
public float GradientAngle { get; init; }
}Optional styling for the DrawSymbol overload. Construct it with a tint and set
only the extras you need:
canvas.DrawSymbol("volume-mute", x, y, 16, 16,
new SymbolStyle(MuteColor) { Outlined = true, OutlineColor = PluginColor.Black, OutlineWidth = 1.5f });public enum TextHAlign { Left, Center, Right }
public enum TextVAlign { Top, Middle, Bottom }Passed to the second DrawText overload for independent horizontal/vertical
alignment within the text box.
Draw with host primitives so your output matches the core, return true when
you painted something and false to defer to the host's default. Reach for
DrawImage(bytes) only when the primitives can't express what you need.
Getting started
API reference
Advanced
Operations
Release notes