epiphany/crates/epiphany-layout-ir/src/render.rs

151 lines
4.9 KiB
Rust

//! Stage 4 — `RenderIR` (Chapter 7 §"RenderIR (Interface Only)").
//!
//! The renderer's input. Its full specification (page templates, draw calls,
//! rasterization) is delivered separately; this module defines only the
//! interface contract and the one obligation that crosses it: "every renderer
//! primitive MUST be traceable to its originating `ResolvedGlyph` (and therefore
//! to its score graph source)" (Chapter 7 §"RenderIR"). v0 implements that
//! interface — provenance and position flow through — and performs **no actual
//! rendering** (QUICKSTART, Agent E).
use crate::provenance::Provenance;
use crate::resolved::ResolvedLayoutIR;
use crate::spatial::{Point, ScaleContext};
use crate::{BoundingBox, Curve, GlyphReference, GlyphStyle, Stroke, Transform2D};
/// A single renderer primitive (Chapter 7 §"RenderIR"). Interface only — it
/// carries just enough to prove the provenance-preservation contract: every
/// primitive traces back to its `ResolvedGlyph`'s source.
#[derive(Clone, PartialEq, Debug)]
pub struct RenderPrimitive {
pub provenance: Provenance,
/// The SMuFL glyph to draw — what the renderer needs to know to produce
/// output (Chapter 7 §"RenderIR": every primitive is traceable to its
/// `ResolvedGlyph`, including its glyph reference).
pub glyph: GlyphReference,
pub position: Point,
pub transform: Option<Transform2D>,
pub bounding_box: BoundingBox,
pub style: GlyphStyle,
pub layer: i32,
}
/// The render IR interface output (Chapter 7 §"RenderIR").
#[derive(Clone, PartialEq, Debug)]
pub struct RenderIR {
pub primitives: Vec<RenderPrimitive>,
/// Non-glyph line primitives (staff lines, stems, barlines, …), traced like
/// the glyph primitives so the round-trip recovers their sources too.
pub strokes: Vec<Stroke>,
/// Cubic-bézier curve primitives (slurs, …), traced like the strokes.
pub curves: Vec<Curve>,
}
/// The render target (Chapter 7 §"RenderIR": `RenderConfiguration.target`).
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum RenderTarget {
Pdf,
Svg,
Screen,
Print,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ColorSpace {
Srgb,
DisplayP3,
Cmyk,
Grayscale,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ColorConfiguration {
pub color_space: ColorSpace,
pub embed_profile: bool,
}
impl Default for ColorConfiguration {
fn default() -> Self {
ColorConfiguration {
color_space: ColorSpace::Srgb,
embed_profile: true,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct RasterizationConfiguration {
pub antialias: bool,
pub dpi: u32,
}
impl Default for RasterizationConfiguration {
fn default() -> Self {
RasterizationConfiguration {
antialias: true,
dpi: 300,
}
}
}
/// Render configuration (Chapter 7 §"RenderIR": `RenderConfiguration`).
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct RenderConfiguration {
pub target: RenderTarget,
pub color: ColorConfiguration,
pub rasterization: RasterizationConfiguration,
}
/// The render-projection interface (Chapter 7 §"RenderIR": `RenderIRProducer`).
/// The spec's `produce(resolved, scale, config)` signature is honored; v0
/// performs **no** rendering (the primitive vocabulary belongs to the
/// out-of-core renderer) but guarantees provenance and glyph identity flow
/// across the boundary.
pub trait RenderIRProducer {
/// Converts resolved IR to renderer-bound primitives, preserving provenance.
fn produce(
&self,
resolved: &ResolvedLayoutIR,
scale: ScaleContext,
config: RenderConfiguration,
) -> RenderIR;
}
/// The v0 render producer: one primitive per resolved glyph, provenance, glyph
/// identity, and position preserved, no rendering performed. The `scale` and
/// `config` are accepted (interface fidelity) but not consumed in v0.
pub struct PassthroughRenderProducer;
impl RenderIRProducer for PassthroughRenderProducer {
fn produce(
&self,
resolved: &ResolvedLayoutIR,
_scale: ScaleContext,
_config: RenderConfiguration,
) -> RenderIR {
to_render(resolved)
}
}
/// The RenderIR interface call (Chapter 7 §"RenderIR"): one primitive per
/// resolved glyph, provenance and position preserved.
pub fn to_render(resolved: &ResolvedLayoutIR) -> RenderIR {
RenderIR {
primitives: resolved
.glyphs
.iter()
.map(|g| RenderPrimitive {
provenance: g.provenance.clone(),
glyph: g.glyph.clone(),
position: g.position,
transform: g.transform,
bounding_box: g.bounding_box,
style: g.style,
layer: g.layer,
})
.collect(),
strokes: resolved.strokes.clone(),
curves: resolved.curves.clone(),
}
}