incular-rendering 0.1.0

Renderer-neutral scene, canvas, display-list, and compositor APIs for Incular
Documentation

incular-rendering

Architecture and support

Contract Status
Ownership Renderer-neutral commands, paths, canvas and retained compositor layers.
API class backend; re-exports and path overrides follow the architecture contract.
Support Available; backend resources belong to WGPU.

incular-rendering owns Incular's renderer-neutral presentation boundary: paths, brushes, ordered display lists, canvas recording, and retained compositor layers. It intentionally contains no window, surface, or GPU state.

Paths are retained as kurbo::BezPath values: Kurbo supplies tight quadratic and cubic bounds plus winding queries, while incular-wgpu translates those same elements directly to Lyon for tessellation. incular_core::Transform uses Kurbo affine composition/inversion while retaining Incular's f32 Offset and Rect values at the public layout boundary.

Backends such as incular-wgpu consume these values and remain responsible for physical DPI conversion, resource allocation, and submission. The public facade also exposes this crate as incular::painting for application-facing naming.

Display lists can also carry balanced SurfacePartitionId markers. They are renderer-neutral no-ops during ordinary in-view rendering, but a multi-surface host can detach selected retained subtrees while preserving painter order and nested partition ownership. This is the boundary used by desktop transient surfaces: rendering owns partition identity/command separation, while native window creation and GPU-surface policy remain outside this crate.

Display-list and compositor contract

incular-rendering owns compact ordered DisplayList and PaintCommand values. It depends on renderer-neutral core/assets types and contains no GPU state. GlyphRun holds the selected font and shaped glyph positions rather than characters, so a backend can rasterize/draw without reshaping. Render objects cache local lists; backends must preserve command order, transforms, and rectangular clips when lowering rectangle and glyph operations.

PaintCommand::Image carries an ImageHandle, a pixel-space source rectangle, and a local logical destination rectangle. It follows normal painter order, transforms, and clipping without exposing GPU resources.

LayerTree stores stable generational Picture, Transform, and ClipRect layers. Pictures retain Arc<DisplayList> payloads, transforms and clips change independently, and flattening produces only the transient ordered submission stream. Bounds-driven rectangular culling is conservative.

Coordinate spaces and transform composition

Pictures contain local logical commands only. Parent-relative layout placement and compositor-only movement are transform layers, accumulated by flatten to world logical coordinates exactly once. Clip rectangles are transformed into that same world space before culling. flattened_pictures() provides the local bounds, world bounds, and active clip observed during the latest flattening for CPU regression tests; physical DPI conversion belongs exclusively to a backend.

Retained opacity isolation

LayerTree also retains an Opacity isolation layer. It owns only a normalized alpha value and a stable content generation; it never owns a GPU texture or render pass. Flattening emits PushOpacity/PopOpacity around the child stream together with conservative subtree bounds and a generation. A backend can therefore render the child stream into a tight transparent target and composite that result once, while an alpha-only change remains a compositor update. A clip inside the layer is part of the cached child stream; a clip above it remains active when the group quad is composited.

Gaussian effects

GaussianBlur and DropShadowEffect are renderer-neutral descriptions. Their sigma values are logical pixels and are normalized (< 0 and non-finite values become zero); the backend multiplies them by the active DPI scale before building a kernel. LayerTree emits PushBlur/PushDropShadow and a matching PopEffect, while the source subtree's generation remains independent of its effect parameters. This lets a compositor update sigma, shadow offset, or shadow color without repainting the child display list. A zero-sigma blur is an identity and does not require an isolation target.

Bounds use a finite three-sigma support: a blur expands a source by 3 * sigma_x/y, and a shadow unions the source with the offset, expanded source rectangle. These are conservative logical bounds; physical target dimensions are rounded outward by the backend. Effects do not change hit testing or semantics geometry, and they contain no renderer or GPU resources.

Effect layers preserve command order and nesting. Clips inside an effect are rendered into its isolated source; clips above the effect remain active when the final result is composited. The source is therefore still an ordinary retained display-list subtree rather than a special box-only shadow primitive.

Color filters, blend modes, and effect chains

ColorFilter is a renderer-neutral 4×5 matrix over normalized straight RGBA. apply uses zero RGB for transparent input, clamps/sanitizes all results, and zeros RGB when the output alpha is zero. The GPU converts retained premultiplied pixels to this straight representation, applies the matrix, and premultiplies the result again. identity, grayscale, sepia, brightness, contrast, saturate, invert, and opacity cover common adjustments. ColorFilter::then composes B(A(x)) as B * A, including the affine bias.

EffectChain is an ordered list of single-input stages. optimized fuses only adjacent color matrices; blur remains a separate spatial stage, so color → blur → color cannot be reordered or collapsed. Drop shadows remain a separate multi-output layer because they emit both a shadow and the original source. Pure color and blend stages do not expand visual bounds.

BlendMode defines Porter–Duff and separable artistic modes over normalized premultiplied pixels. The public blend_premultiplied reference follows the alpha-aware equation used by the GPU and is safe for zero-alpha inputs. A blend-mode property belongs to the final composite state: it does not change the source generation or invalidate upstream filter pixels.