Prowl.Quill is completely decoupled from any specific graphics API. All rendering passes through a single interface —Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ProwlEngine/Prowl.Quill/llms.txt
Use this file to discover all available pages before exploring further.
ICanvasRenderer — which you implement once for your target platform. You could write a backend for Vulkan, Metal, DirectX 11/12, a software rasteriser, or any other rendering system without changing a single line of Quill’s core tessellation code. This guide walks you through the full interface, explains what each method and property must do, and provides an annotated skeleton you can use as a starting point.
The Full Interface
ICanvasRenderer extends IDisposable. Always release GPU resources (buffers, textures, shaders, framebuffers) in your Dispose implementation.Understanding the DrawCall Model
When you call canvas.Render(), Quill calls RenderCalls(canvas, drawCalls) with the complete vertex buffer (canvas.Vertices), index buffer (canvas.Indices), and a list of DrawCall objects. All draw calls share the same vertex and index buffers — each DrawCall identifies its slice of the index buffer via ElementCount, which is a count of individual index values (always a multiple of 3 for triangles).
i, you must first accumulate the total index offset from all previous draw calls, then issue a draw using drawCall.ElementCount indices starting at that offset:
The Brush — Fill Style
Each DrawCall exposes a Brush that describes how the shape should be filled:
| Property | Type | Description |
|---|---|---|
Type | BrushType | None, Linear, Radial, or Box |
Color1 / Color2 | Color32 | Start and end gradient colours (RGBA) |
BrushMatrix | Float4x4 | Transform applied to the fill coordinate space |
Point1 / Point2 | Float2 | Gradient control points (start/end for linear, center/radius for radial) |
CornerRadii | double | Box gradient corner radius |
Feather | double | Box gradient feather width |
TextureMatrix | Float4x4 | Transform for texture-coordinate sampling |
BackdropBlur | double | Blur radius; > 0 triggers frosted-glass mode |
brushType, brushMat, brushColor1, brushColor2, brushParams, brushParams2, brushTextureMat) so you can lift the GLSL fragment shader from CanvasShaderSource.FragmentShader with minimal modification.
The Scissor — Clipping Region
CalldrawCall.GetScissor(out Float4x4 scissorMat, out Float2 scissorExt) to retrieve the active scissor. The scissor is represented as a centre/half-extents pair in transformed space rather than as a raw pixel rectangle, which allows rotated clip regions. Pass both values to the shader; when scissorExt.X < 0, scissoring is disabled and the shader returns 1.0 for the mask unconditionally.
Custom Shaders and Uniforms
DrawCall.Shader is an object? that may hold a backend-specific shader handle (e.g. int program in OpenGL, uint in Silk.NET). When non-null, switch to that shader instead of the default one. Extra data for the custom shader is provided via DrawCall.ShaderUniforms, which exposes a Dictionary<string, object> of typed values:
Texture Binding
DrawCall.Texture is an object? that holds whatever value your CreateTexture method returned. When null, bind a fallback 1×1 white texture so the shader can still sample safely.
Complete Skeleton Implementation
Implement texture management
Quill calls
CreateTexture when it needs to allocate the glyph atlas or a user texture. The returned object is treated as an opaque handle and passed back into GetTextureSize and SetTextureData.Upload geometry and configure vertex layout
Inside
RenderCalls, upload canvas.Vertices and canvas.Indices to the GPU once per frame. Each Vertex is 20 bytes: float x, y, u, v followed by byte r, g, b, a.Iterate draw calls and set per-draw-call state
For each
DrawCall, bind the correct texture, switch shaders if necessary, upload uniforms, and issue the indexed draw call.Reusing the Built-In GLSL Shaders
If your backend targets OpenGL or WebGL, you can use the shared shaders fromCanvasShaderSource directly:
SupportsBackdropBlur = true.
Key Rules Checklist
Always return the same object from CreateTexture
Quill stores the returned object and passes it back verbatim. Use a stable wrapper type; never return a value type boxed twice.
Upload geometry once per frame
canvas.Vertices and canvas.Indices are valid only for the duration of the RenderCalls call. Do not cache them across frames.Respect indexOffset
Draw calls share a single index buffer. Keep a running
indexOffset counter and advance it by drawCall.ElementCount after every draw.Use premultiplied alpha blending
Set blend factors to
(ONE, ONE_MINUS_SRC_ALPHA). Quill outputs premultiplied alpha. Standard SRC_ALPHA, ONE_MINUS_SRC_ALPHA blending will produce incorrect results.Bind a white texture when Texture is null
A null
DrawCall.Texture means the draw call has no user texture. Bind a 1×1 white texture so the shader’s texture sample returns vec4(1) and the brush colour is applied correctly.Disable depth testing
Quill uses the painter’s algorithm — draw calls are ordered back-to-front. Depth testing will cause shapes to clip through each other unexpectedly.