use super::paint::{PaintItem, Placement};
use crate::TextTexture;
use alpha2_render::{
BlendMode, GraphError, GraphLayer, LayerTransform, RenderError, RenderGraph, RenderNodeId,
RenderSize,
};
use std::sync::Arc;
#[derive(Debug)]
pub struct PreparedLayout {
pub(crate) items: Vec<PaintItem>,
pub(crate) texts: Vec<Option<u64>>,
}
#[derive(Clone, Debug)]
pub(crate) struct LaidOut<'t> {
pub(crate) layout: Arc<PreparedLayout>,
pub(crate) texts: Vec<Option<TextTexture<'t>>>,
}
impl<'t> LaidOut<'t> {
pub(crate) fn attach(
layout: Arc<PreparedLayout>,
frames: &'t dyn crate::FrameProvider,
) -> Option<Self> {
let texts = layout
.texts
.iter()
.map(|key| {
key.map(|key| frames.text(key))
.map_or(Some(None), |text| text.map(Some))
})
.collect::<Option<Vec<_>>>()?;
Some(Self { layout, texts })
}
pub(crate) fn add_to(&self, graph: &mut RenderGraph<'t>) -> Result<RenderNodeId, RenderError> {
let layers = self.layers(graph, &self.layout.items)?;
graph
.add_composite(layers)
.map_err(GraphError::into_render_error)
}
fn layers(
&self,
graph: &mut RenderGraph<'t>,
items: &[PaintItem],
) -> Result<Vec<GraphLayer>, RenderError> {
let size = graph.size();
let canvas = [size.width, size.height];
let mut layers = Vec::with_capacity(items.len());
for item in items {
let (source, place, extent) = match item {
PaintItem::Vector { scene, place } => {
(graph.add_vector(Arc::clone(scene)), *place, canvas)
}
PaintItem::Text { index, place } => {
let texture = self
.texts
.get(*index)
.copied()
.flatten()
.ok_or(RenderError::MissingText)?;
let source = match texture.cache_key {
Some(key) => graph.add_source_with_key(texture.texture, key),
None => graph.add_source(texture.texture),
};
(source, *place, [texture.width, texture.height])
}
PaintItem::Group { items, clip, place } => {
let inner = self.layers(graph, items)?;
let mut node = graph
.add_composite(inner)
.map_err(GraphError::into_render_error)?;
if let Some(clip) = clip {
let coverage = graph.add_vector(Arc::clone(clip));
node = crate::frame::coverage_mask(
graph,
node,
&[(coverage, false, alpha2_model::MaskMode::Add)],
)?;
}
(node, *place, canvas)
}
};
layers.push(GraphLayer {
source,
visual: layer_transform(place, extent, size),
blend_mode: BlendMode::Normal,
});
}
Ok(layers)
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
fn layer_transform(place: Placement, extent: [u32; 2], size: RenderSize) -> LayerTransform {
let output = [f64::from(size.width), f64::from(size.height)];
let extent = extent.map(f64::from);
LayerTransform {
position: [
(place.position[0] / output[0]) as f32,
(place.position[1] / output[1]) as f32,
],
anchor: [
(place.anchor[0] / extent[0]) as f32,
(place.anchor[1] / extent[1]) as f32,
],
scale: [
(place.scale[0] * extent[0] / output[0]) as f32,
(place.scale[1] * extent[1] / output[1]) as f32,
],
rotation_degrees: place.rotation_degrees as f32,
opacity: place.opacity,
}
}