freya_core/
render_pipeline.rs

1use freya_engine::prelude::{
2    Canvas,
3    FontCollection,
4    FontMgr,
5    SkMatrix,
6    SkPoint,
7    SkRect,
8};
9
10use crate::{
11    element::{
12        ClipContext,
13        RenderContext,
14    },
15    prelude::Color,
16    tree::Tree,
17};
18
19pub struct RenderPipeline<'a> {
20    pub font_collection: &'a mut FontCollection,
21    pub font_manager: &'a FontMgr,
22    pub canvas: &'a Canvas,
23    pub tree: &'a Tree,
24    pub scale_factor: f64,
25    pub background: Color,
26}
27
28impl RenderPipeline<'_> {
29    #[cfg_attr(feature = "hotpath", hotpath::measure)]
30    pub fn render(self) {
31        self.canvas.clear(self.background);
32
33        // TODO: Use incremental rendering
34        for i16 in itertools::sorted(self.tree.layers.keys()) {
35            let nodes = self.tree.layers.get(i16).unwrap();
36            'rendering: for node_id in nodes {
37                let layer = self.canvas.save();
38
39                let element = self.tree.elements.get(node_id).unwrap();
40                let text_style_state = self.tree.text_style_state.get(node_id).unwrap();
41                let layout_node = self.tree.layout.get(node_id).unwrap();
42                let effect_state = self.tree.effect_state.get(node_id);
43
44                if let Some(effect_state) = effect_state {
45                    let mut visible_area = layout_node.visible_area();
46
47                    // Transform the element area given the scale effects
48                    for id in effect_state.scales.iter() {
49                        let layout_node = self.tree.layout.get(id).unwrap();
50                        let effect = self.tree.effect_state.get(id).unwrap();
51                        let area = layout_node.visible_area();
52                        let center = area.center();
53                        let scale = effect.scale.unwrap();
54
55                        visible_area = visible_area.translate(-center.to_vector());
56                        visible_area = visible_area.scale(scale.x, scale.y);
57                        visible_area = visible_area.translate(center.to_vector());
58                    }
59
60                    hotpath::measure_block!("Element Clipping", {
61                        for clip_node_id in effect_state.clips.iter() {
62                            let clip_element = self.tree.elements.get(clip_node_id).unwrap();
63                            let clip_layout_node = self.tree.layout.get(clip_node_id).unwrap();
64                            let clip_effect = self.tree.effect_state.get(clip_node_id).unwrap();
65
66                            let mut transformed_clip_area = clip_layout_node.visible_area();
67
68                            // For every clip area that his element gets we also need to apply the effects to each one so that
69                            // we can properly assume whether this element is actually visible or not
70                            for id in clip_effect.scales.iter() {
71                                let scale_layout_node = self.tree.layout.get(id).unwrap();
72                                let scale_effect = self.tree.effect_state.get(id).unwrap();
73                                let area = scale_layout_node.visible_area();
74                                let center = area.center();
75                                let scale = scale_effect.scale.unwrap();
76
77                                transformed_clip_area =
78                                    transformed_clip_area.translate(-center.to_vector());
79                                transformed_clip_area =
80                                    transformed_clip_area.scale(scale.x, scale.y);
81                                transformed_clip_area =
82                                    transformed_clip_area.translate(center.to_vector());
83                            }
84
85                            // No need to render this element as it is completely clipped
86                            if !visible_area.intersects(&transformed_clip_area) {
87                                self.canvas.restore_to_count(layer);
88                                continue 'rendering;
89                            }
90
91                            let clip_context = ClipContext {
92                                canvas: self.canvas,
93                                visible_area: &transformed_clip_area,
94                                scale_factor: self.scale_factor,
95                            };
96
97                            clip_element.clip(clip_context);
98                        }
99                    });
100
101                    // Pass rotate effect to children
102                    for id in effect_state.rotations.iter() {
103                        let layout_node = self.tree.layout.get(id).unwrap();
104                        let effect = self.tree.effect_state.get(id).unwrap();
105                        let area = layout_node.visible_area();
106                        let mut matrix = SkMatrix::new_identity();
107                        matrix.set_rotate(
108                            effect.rotation.unwrap(),
109                            Some(SkPoint {
110                                x: area.min_x() + area.width() / 2.0,
111                                y: area.min_y() + area.height() / 2.0,
112                            }),
113                        );
114                        self.canvas.concat(&matrix);
115                    }
116
117                    // Apply inherited opacity effects
118                    let rect = SkRect::new(
119                        visible_area.min_x(),
120                        visible_area.min_y(),
121                        visible_area.max_x(),
122                        visible_area.max_y(),
123                    );
124                    for opacity in effect_state.opacities.iter() {
125                        self.canvas.save_layer_alpha_f(rect, *opacity);
126                    }
127
128                    // Transform the canvas area given the scale effects
129                    for id in effect_state.scales.iter() {
130                        let layout_node = self.tree.layout.get(id).unwrap();
131                        let effect = self.tree.effect_state.get(id).unwrap();
132                        let area = layout_node.visible_area();
133                        let center = area.center();
134                        let scale = effect.scale.unwrap();
135
136                        self.canvas.translate((center.x, center.y));
137                        self.canvas.scale((scale.x, scale.y));
138                        self.canvas.translate((-center.x, -center.y));
139                    }
140                }
141
142                let render_context = RenderContext {
143                    font_collection: self.font_collection,
144                    canvas: self.canvas,
145                    layout_node,
146                    tree: self.tree,
147                    text_style_state,
148                    scale_factor: self.scale_factor,
149                };
150
151                hotpath::measure_block!("Element Render", {
152                    element.render(render_context);
153                });
154
155                self.canvas.restore_to_count(layer);
156            }
157        }
158    }
159}