1use freya_engine::prelude::{
2 ClipOp,
3 Color,
4 FontCollection,
5 FontMgr,
6 Matrix,
7 Point,
8 Rect,
9 SamplingOptions,
10 Surface,
11};
12use freya_native_core::{
13 node::{
14 ElementNode,
15 NodeType,
16 },
17 real_dom::NodeImmutable,
18 tags::TagName,
19 NodeId,
20};
21use itertools::sorted;
22use torin::prelude::{
23 Area,
24 LayoutNode,
25 Torin,
26};
27
28use super::{
29 wireframe_renderer,
30 Compositor,
31 CompositorCache,
32 CompositorDirtyArea,
33};
34use crate::{
35 dom::{
36 CompositorDirtyNodes,
37 DioxusDOM,
38 DioxusNode,
39 ImagesCache,
40 },
41 elements::{
42 ElementUtils,
43 ElementUtilsResolver,
44 },
45 layers::Layers,
46 states::{
47 TransformState,
48 ViewportState,
49 },
50};
51
52pub struct RenderPipeline<'a> {
54 pub rdom: &'a DioxusDOM,
55 pub layers: &'a Layers,
56 pub layout: &'a Torin<NodeId>,
57 pub compositor_dirty_nodes: &'a mut CompositorDirtyNodes,
58 pub compositor_dirty_area: &'a mut CompositorDirtyArea,
59 pub compositor_cache: &'a mut CompositorCache,
60 pub surface: &'a mut Surface,
61 pub dirty_surface: &'a mut Surface,
62 pub compositor: &'a mut Compositor,
63 pub font_collection: &'a mut FontCollection,
64 pub font_manager: &'a FontMgr,
65 pub images_cache: &'a mut ImagesCache,
66 pub canvas_area: Area,
67 pub background: Color,
68 pub scale_factor: f32,
69 pub selected_node: Option<NodeId>,
70 pub default_fonts: &'a [String],
71}
72
73impl RenderPipeline<'_> {
74 pub fn run(&mut self) {
75 let mut dirty_layers = Layers::default();
76
77 let rendering_layers = self.compositor.run(
79 self.compositor_dirty_nodes,
80 self.compositor_dirty_area,
81 self.compositor_cache,
82 self.layers,
83 &mut dirty_layers,
84 self.layout,
85 self.rdom,
86 self.scale_factor,
87 );
88
89 #[cfg(feature = "fade-cached-incremental-areas")]
90 {
91 if self.compositor_dirty_area.is_some() {
93 use freya_engine::prelude::{
94 Paint,
95 PaintStyle,
96 };
97 let rect = Rect::new(
98 self.canvas_area.min_x(),
99 self.canvas_area.min_y(),
100 self.canvas_area.max_x(),
101 self.canvas_area.max_y(),
102 );
103 let mut paint = Paint::default();
104 paint.set_color(Color::from_argb(10, 245, 245, 245));
105 paint.set_anti_alias(true);
106 paint.set_style(PaintStyle::Fill);
107 self.dirty_surface.canvas().draw_rect(rect, &paint);
108 }
109 }
110
111 self.dirty_surface.canvas().save();
112
113 self.compositor_dirty_area.round_out();
115
116 if let Some(dirty_area) = self.compositor_dirty_area.take() {
119 #[cfg(debug_assertions)]
120 tracing::info!("Marked {dirty_area:?} as dirty area");
121
122 self.dirty_surface.canvas().clip_rect(
123 Rect::new(
124 dirty_area.min_x(),
125 dirty_area.min_y(),
126 dirty_area.max_x(),
127 dirty_area.max_y(),
128 ),
129 ClipOp::Intersect,
130 false,
131 );
132 self.dirty_surface.canvas().clear(self.background);
133 }
134
135 #[cfg(debug_assertions)]
136 let mut painted = 0;
138
139 for (_, nodes) in sorted(rendering_layers.iter()) {
141 'elements: for node_id in sorted(nodes) {
142 let node_ref = self.rdom.get(*node_id).unwrap();
143 let node_viewports = node_ref.get::<ViewportState>().unwrap();
144 let layout_node = self.layout.get(*node_id);
145
146 if let Some(layout_node) = layout_node {
147 for viewport_id in &node_viewports.viewports {
149 let viewport = self.layout.get(*viewport_id).unwrap().visible_area();
150 if !viewport.intersects(&layout_node.area) {
151 continue 'elements;
152 }
153 }
154
155 self.render(node_ref, layout_node);
157
158 #[cfg(debug_assertions)]
159 {
160 painted += 1;
161 }
162 }
163 }
164 }
165
166 if let Some(selected_node) = &self.selected_node {
167 if let Some(layout_node) = self.layout.get(*selected_node) {
168 wireframe_renderer::render_wireframe(
169 self.dirty_surface.canvas(),
170 &layout_node.visible_area(),
171 );
172 }
173 }
174
175 #[cfg(debug_assertions)]
176 {
177 if painted > 0 {
178 tracing::info!("Painted {painted} nodes");
179 }
180 }
181
182 self.dirty_surface.canvas().restore();
184 self.surface.canvas().clear(self.background);
185 self.dirty_surface.draw(
186 self.surface.canvas(),
187 (0, 0),
188 SamplingOptions::default(),
189 None,
190 );
191
192 self.compositor_dirty_nodes.clear();
193 }
194
195 pub fn render(&mut self, node_ref: DioxusNode, layout_node: &LayoutNode) {
196 let dirty_canvas = self.dirty_surface.canvas();
197 let node_type = &*node_ref.node_type();
198 if let NodeType::Element(ElementNode { tag, .. }) = node_type {
199 let Some(element_utils) = tag.utils() else {
200 return;
201 };
202
203 let initial_layer = dirty_canvas.save();
204
205 let node_transform = &*node_ref.get::<TransformState>().unwrap();
206 let node_viewports = node_ref.get::<ViewportState>().unwrap();
207
208 for node_id in &node_viewports.viewports {
209 let node_ref = self.rdom.get(*node_id).unwrap();
210 let node_type = node_ref.node_type();
211 let Some(element_utils) = node_type.tag().and_then(|tag| tag.utils()) else {
212 continue;
213 };
214 let layout_node = self.layout.get(*node_id).unwrap();
215 element_utils.clip(layout_node, &node_ref, dirty_canvas, self.scale_factor);
216 }
217
218 for (id, scale_x, scale_y) in &node_transform.scales {
220 let layout_node = self.layout.get(*id).unwrap();
221 let area = layout_node.visible_area();
222 let center = area.center();
223 dirty_canvas.translate((center.x, center.y));
224 dirty_canvas.scale((*scale_x, *scale_y));
225 dirty_canvas.translate((-center.x, -center.y));
226 }
227
228 for (id, rotate_degs) in &node_transform.rotations {
230 let layout_node = self.layout.get(*id).unwrap();
231 let area = layout_node.visible_area();
232 let mut matrix = Matrix::new_identity();
233 matrix.set_rotate(
234 *rotate_degs,
235 Some(Point {
236 x: area.min_x() + area.width() / 2.0,
237 y: area.min_y() + area.height() / 2.0,
238 }),
239 );
240 dirty_canvas.concat(&matrix);
241 }
242
243 for opacity in &node_transform.opacities {
245 dirty_canvas.save_layer_alpha_f(
246 Rect::new(
247 self.canvas_area.min_x(),
248 self.canvas_area.min_y(),
249 self.canvas_area.max_x(),
250 self.canvas_area.max_y(),
251 ),
252 *opacity,
253 );
254 }
255
256 if *tag == TagName::Paragraph || *tag == TagName::Label || *tag == TagName::Image {
259 element_utils.clip(layout_node, &node_ref, dirty_canvas, self.scale_factor);
260 }
261
262 element_utils.render(
263 layout_node,
264 &node_ref,
265 dirty_canvas,
266 self.font_collection,
267 self.font_manager,
268 self.default_fonts,
269 self.images_cache,
270 self.scale_factor,
271 );
272
273 dirty_canvas.restore_to_count(initial_layer);
274 }
275 }
276}