Skip to content

Conversation

@waywardmonkeys
Copy link
Contributor

@waywardmonkeys waywardmonkeys commented Nov 30, 2025

Add understory_imaging imaging IR crate

This PR adds understory_imaging, a backend‑agnostic imaging IR that sits between the presentation/display layer and concrete render backends (Vello CPU/Hybrid/Classic, Skia, etc.).

The crate defines:

  • Opaque resource handles: PathId, ImageId, PaintId, PictureId, FilterId with PathDesc, ImageDesc,
    PaintDesc, PictureDesc descriptors, managed via a ResourceBackend trait. IDs are small, stable handles reused
    across frames and recordings for the lifetime of each resource.
  • Imaging operations: StateOp (transform, paint, stroke, clip, blend, opacity, groups) and DrawOp (fill/
    stroke path/rect, draw image/picture), combined into ImagingOp as a POD‑friendly IR suitable for recording
    and caching.
  • Backend interface: an ImagingBackend trait that extends ResourceBackend with state/draw entry points and a
    basic recording API (begin_record/end_record), plus helpers record_ops and record_picture to capture short
    sequences as reusable recordings or pictures.
  • Recordings model: RecordedOps wraps an Arc<[ImagingOp]> plus optional backend‑specific acceleration and
    transform metadata (valid_under: TransformClass, original_ctm). Recordings are explicitly environment‑bound:
    all referenced resource IDs must exist and remain compatible when replayed.
  • Transform classification: TransformClass and transform_diff_class provide a conservative way to decide when
    cached/recorded content can be safely reused under a new transform (distinguishing exact, translate‑only, and
    general affine differences).

The crate is no_std and currently targets the needs of early Vello/Skia backends. The overall goals, architecture, and future directions are documented in docs/issue_understory_imaging.md; the crate‑level docs give a condensed overview and a minimal usage sketch for backend implementors.

Comment on lines +75 to +94
PushClip {
/// Common header metadata.
header: OpHeader,
/// Identifier of the clip shape to push.
clip: ClipId,
},
/// End a clip stack entry.
PopClip {
/// Common header metadata.
header: OpHeader,
},
/// Begin a compositing group with optional opacity and blend mode.
Group {
/// Common header metadata.
header: OpHeader,
/// Optional opacity multiplier in [0, 1] for the group.
opacity: f32,
/// Blend mode used when compositing the group into its parent.
blend: BlendMode,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be a push group and a pop group and that clip is just an op that modifies a group. same thing with blend mode and opacity and things like alpha and luminance masks. these should be ops that affect a group. groups get pushed and popped. then when translating to backends, there can be optimization logic for the best api to use given the set of modifiers on a group.

maybe even have an op for group modifiers with a set

Group {
    header: OpHeader,
    modifiers: Set<GroupModifier>,
}

enum GroupModifier {
    Clip(ClipId),
    Opacity(f32),
    AlphaMask(ImageId),
    LuminanceMask(ImageId),
    BlendMode(BlendMode),
    Transform(Affine),
}


/// Clip shape used by `StateOp::SetClip`.
///
/// This will likely grow variants for rounded rects and more complex regions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed! ClipShape::RoundedRect is already in

let transform =
imaging::Affine::translate((tx, ty)) * imaging::Affine::scale_non_uniform(w, h);

if (*opacity - 1.0).abs() > f32::EPSILON {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if opacity needs to be reset (back to 1.0) to avoid leaking state across display-list entries...the conditional SetOpacity only fires when opacity != 1, so an image can drop opacity and nothing bumps it back, leaving later ops dimmer than intended.

@waywardmonkeys waywardmonkeys changed the title WIP understory_imaging Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants