Skip to main content

alpha2_engine/layout/
draw.rs

1//! A prepared layout drawn into the render graph as one composite over transparency.
2
3use super::paint::{PaintItem, Placement};
4use crate::TextTexture;
5use alpha2_render::{
6    BlendMode, GraphError, GraphLayer, LayerTransform, RenderError, RenderGraph, RenderNodeId,
7    RenderSize,
8};
9use std::sync::Arc;
10
11/// A layout resolved at one render size: its paint list and each text node's texture key.
12///
13/// Prepared by [`crate::TextTextures`] and lent via [`crate::FrameProvider::layout`].
14#[derive(Debug)]
15pub struct PreparedLayout {
16    pub(crate) items: Vec<PaintItem>,
17    pub(crate) texts: Vec<Option<u64>>,
18}
19
20/// A prepared layout with the textures its text nodes draw.
21#[derive(Clone, Debug)]
22pub(crate) struct LaidOut<'t> {
23    pub(crate) layout: Arc<PreparedLayout>,
24    pub(crate) texts: Vec<Option<TextTexture<'t>>>,
25}
26
27impl<'t> LaidOut<'t> {
28    /// `layout` with its textures from `frames`, or `None` when one is missing.
29    pub(crate) fn attach(
30        layout: Arc<PreparedLayout>,
31        frames: &'t dyn crate::FrameProvider,
32    ) -> Option<Self> {
33        let texts = layout
34            .texts
35            .iter()
36            .map(|key| {
37                key.map(|key| frames.text(key))
38                    .map_or(Some(None), |text| text.map(Some))
39            })
40            .collect::<Option<Vec<_>>>()?;
41        Some(Self { layout, texts })
42    }
43
44    pub(crate) fn add_to(&self, graph: &mut RenderGraph<'t>) -> Result<RenderNodeId, RenderError> {
45        let layers = self.layers(graph, &self.layout.items)?;
46        graph
47            .add_composite(layers)
48            .map_err(GraphError::into_render_error)
49    }
50
51    fn layers(
52        &self,
53        graph: &mut RenderGraph<'t>,
54        items: &[PaintItem],
55    ) -> Result<Vec<GraphLayer>, RenderError> {
56        let size = graph.size();
57        let canvas = [size.width, size.height];
58        let mut layers = Vec::with_capacity(items.len());
59        for item in items {
60            let (source, place, extent) = match item {
61                PaintItem::Vector { scene, place } => {
62                    (graph.add_vector(Arc::clone(scene)), *place, canvas)
63                }
64                PaintItem::Text { index, place } => {
65                    let texture = self
66                        .texts
67                        .get(*index)
68                        .copied()
69                        .flatten()
70                        .ok_or(RenderError::MissingText)?;
71                    let visual = layer_transform(*place, [texture.width, texture.height], size);
72                    layers.push(crate::frame::text_layer(graph, texture, visual)?);
73                    continue;
74                }
75                PaintItem::Group { items, clip, place } => {
76                    let inner = self.layers(graph, items)?;
77                    let mut node = graph
78                        .add_composite(inner)
79                        .map_err(GraphError::into_render_error)?;
80                    if let Some(clip) = clip {
81                        let coverage = graph.add_vector(Arc::clone(clip));
82                        node = crate::frame::coverage_mask(
83                            graph,
84                            node,
85                            &[(coverage, false, alpha2_model::MaskMode::Add)],
86                        )?;
87                    }
88                    (node, *place, canvas)
89                }
90            };
91            layers.push(GraphLayer {
92                source,
93                visual: layer_transform(place, extent, size),
94                blend_mode: BlendMode::Normal,
95            });
96        }
97        Ok(layers)
98    }
99}
100
101/// `place` as a layer transform for a source of `extent` pixels drawn 1:1 on `size`.
102#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
103fn layer_transform(place: Placement, extent: [u32; 2], size: RenderSize) -> LayerTransform {
104    let output = [f64::from(size.width), f64::from(size.height)];
105    let extent = extent.map(f64::from);
106    LayerTransform {
107        position: [
108            (place.position[0] / output[0]) as f32,
109            (place.position[1] / output[1]) as f32,
110        ],
111        anchor: [
112            (place.anchor[0] / extent[0]) as f32,
113            (place.anchor[1] / extent[1]) as f32,
114        ],
115        scale: [
116            (place.scale[0] * extent[0] / output[0]) as f32,
117            (place.scale[1] * extent[1] / output[1]) as f32,
118        ],
119        rotation_degrees: place.rotation_degrees as f32,
120        opacity: place.opacity,
121    }
122}