use freya_common::{
CompositorDirtyNodes,
Layers,
};
use freya_engine::prelude::{
ClipOp,
Color,
FontCollection,
FontMgr,
Matrix,
Point,
Rect,
SamplingOptions,
Surface,
};
use freya_native_core::{
node::{
ElementNode,
NodeType,
},
real_dom::NodeImmutable,
tags::TagName,
NodeId,
};
use freya_node_state::{
TransformState,
ViewportState,
};
use itertools::sorted;
use torin::prelude::{
Area,
LayoutNode,
Torin,
};
use super::{
wireframe_renderer,
CompositorCache,
CompositorDirtyArea,
};
use crate::{
dom::{
DioxusDOM,
DioxusNode,
},
prelude::{
Compositor,
ElementUtils,
ElementUtilsResolver,
},
};
pub struct RenderPipeline<'a> {
pub rdom: &'a DioxusDOM,
pub layers: &'a Layers,
pub layout: &'a Torin<NodeId>,
pub compositor_dirty_nodes: &'a mut CompositorDirtyNodes,
pub compositor_dirty_area: &'a mut CompositorDirtyArea,
pub compositor_cache: &'a mut CompositorCache,
pub surface: &'a mut Surface,
pub dirty_surface: &'a mut Surface,
pub compositor: &'a mut Compositor,
pub font_collection: &'a mut FontCollection,
pub font_manager: &'a FontMgr,
pub canvas_area: Area,
pub background: Color,
pub scale_factor: f32,
pub selected_node: Option<NodeId>,
pub default_fonts: &'a [String],
}
impl RenderPipeline<'_> {
pub fn run(&mut self) {
let mut dirty_layers = Layers::default();
let rendering_layers = self.compositor.run(
self.compositor_dirty_nodes,
self.compositor_dirty_area,
self.compositor_cache,
self.layers,
&mut dirty_layers,
self.layout,
self.rdom,
self.scale_factor,
);
#[cfg(feature = "fade-cached-incremental-areas")]
{
if self.compositor_dirty_area.is_some() {
use freya_engine::prelude::{
Paint,
PaintStyle,
};
let rect = Rect::new(
self.canvas_area.min_x(),
self.canvas_area.min_y(),
self.canvas_area.max_x(),
self.canvas_area.max_y(),
);
let mut paint = Paint::default();
paint.set_color(Color::from_argb(10, 245, 245, 245));
paint.set_anti_alias(true);
paint.set_style(PaintStyle::Fill);
self.dirty_surface.canvas().draw_rect(rect, &paint);
}
}
self.dirty_surface.canvas().save();
self.compositor_dirty_area.round_out();
if let Some(dirty_area) = self.compositor_dirty_area.take() {
#[cfg(debug_assertions)]
tracing::info!("Marked {dirty_area:?} as dirty area");
self.dirty_surface.canvas().clip_rect(
Rect::new(
dirty_area.min_x(),
dirty_area.min_y(),
dirty_area.max_x(),
dirty_area.max_y(),
),
ClipOp::Intersect,
false,
);
self.dirty_surface.canvas().clear(self.background);
}
#[cfg(debug_assertions)]
let mut painted = 0;
for (_, nodes) in sorted(rendering_layers.iter()) {
'elements: for node_id in sorted(nodes) {
let node_ref = self.rdom.get(*node_id).unwrap();
let node_viewports = node_ref.get::<ViewportState>().unwrap();
let layout_node = self.layout.get(*node_id);
if let Some(layout_node) = layout_node {
for viewport_id in &node_viewports.viewports {
let viewport = self.layout.get(*viewport_id).unwrap().visible_area();
if !viewport.intersects(&layout_node.area) {
continue 'elements;
}
}
let render_wireframe = Some(node_id) == self.selected_node.as_ref();
self.render(node_ref, layout_node, render_wireframe);
#[cfg(debug_assertions)]
{
painted += 1;
}
}
}
}
#[cfg(debug_assertions)]
{
if painted > 0 {
tracing::info!("Painted {painted} nodes");
}
}
self.dirty_surface.canvas().restore();
self.surface.canvas().clear(self.background);
self.dirty_surface.draw(
self.surface.canvas(),
(0, 0),
SamplingOptions::default(),
None,
);
self.compositor_dirty_nodes.clear();
}
pub fn render(
&mut self,
node_ref: DioxusNode,
layout_node: &LayoutNode,
render_wireframe: bool,
) {
let dirty_canvas = self.dirty_surface.canvas();
let area = layout_node.visible_area();
let node_type = &*node_ref.node_type();
if let NodeType::Element(ElementNode { tag, .. }) = node_type {
let Some(element_utils) = tag.utils() else {
return;
};
let initial_layer = dirty_canvas.save();
let node_transform = &*node_ref.get::<TransformState>().unwrap();
for (id, rotate_degs) in &node_transform.rotations {
let layout_node = self.layout.get(*id).unwrap();
let area = layout_node.visible_area();
let mut matrix = Matrix::new_identity();
matrix.set_rotate(
*rotate_degs,
Some(Point {
x: area.min_x() + area.width() / 2.0,
y: area.min_y() + area.height() / 2.0,
}),
);
dirty_canvas.concat(&matrix);
}
for opacity in &node_transform.opacities {
dirty_canvas.save_layer_alpha_f(
Rect::new(
self.canvas_area.min_x(),
self.canvas_area.min_y(),
self.canvas_area.max_x(),
self.canvas_area.max_y(),
),
*opacity,
);
}
let node_viewports = node_ref.get::<ViewportState>().unwrap();
if !node_viewports.viewports.is_empty() && *tag == TagName::Paragraph {
element_utils.clip(layout_node, &node_ref, dirty_canvas, self.scale_factor);
}
for node_id in &node_viewports.viewports {
let node_ref = self.rdom.get(*node_id).unwrap();
let node_type = node_ref.node_type();
let Some(element_utils) = node_type.tag().and_then(|tag| tag.utils()) else {
continue;
};
let layout_node = self.layout.get(*node_id).unwrap();
element_utils.clip(layout_node, &node_ref, dirty_canvas, self.scale_factor);
}
element_utils.render(
layout_node,
&node_ref,
dirty_canvas,
self.font_collection,
self.font_manager,
self.default_fonts,
self.scale_factor,
);
if render_wireframe {
wireframe_renderer::render_wireframe(dirty_canvas, &area);
}
dirty_canvas.restore_to_count(initial_layer);
}
}
}