freya_core/
render_pipeline.rs1use freya_engine::prelude::{
2 Canvas,
3 ClipOp,
4 FontCollection,
5 FontMgr,
6 SaveLayerRec,
7 SkMatrix,
8 SkPoint,
9 blur,
10};
11
12use crate::{
13 element::{
14 ClipContext,
15 RenderContext,
16 },
17 prelude::Color,
18 tree::Tree,
19};
20
21pub struct RenderPipeline<'a> {
22 pub font_collection: &'a mut FontCollection,
23 pub font_manager: &'a FontMgr,
24 pub canvas: &'a Canvas,
25 pub tree: &'a Tree,
26 pub scale_factor: f64,
27 pub background: Color,
28}
29
30impl RenderPipeline<'_> {
31 #[cfg_attr(feature = "hotpath", hotpath::measure)]
32 pub fn render(self) {
33 self.canvas.clear(self.background);
34
35 for i16 in itertools::sorted(self.tree.layers.keys()) {
37 let nodes = self.tree.layers.get(i16).unwrap();
38 'rendering: for node_id in nodes {
39 let layer = self.canvas.save();
40
41 let element = self.tree.elements.get(node_id).unwrap();
42 let text_style_state = self.tree.text_style_state.get(node_id).unwrap();
43 let layout_node = self.tree.layout.get(node_id).unwrap();
44 let effect_state = self.tree.effect_state.get(node_id);
45
46 if let Some(effect_state) = effect_state {
47 let mut visible_area = layout_node.visible_area();
48
49 for id in effect_state.scales.iter() {
51 let layout_node = self.tree.layout.get(id).unwrap();
52 let effect = self.tree.effect_state.get(id).unwrap();
53 let area = layout_node.visible_area();
54 let center = area.center();
55 let scale = effect.scale.unwrap();
56
57 visible_area = visible_area.translate(-center.to_vector());
58 visible_area = visible_area.scale(scale.x, scale.y);
59 visible_area = visible_area.translate(center.to_vector());
60 }
61
62 hotpath::measure_block!("Element Clipping", {
63 for clip_node_id in effect_state.clips.iter() {
64 let clip_element = self.tree.elements.get(clip_node_id).unwrap();
65 let clip_layout_node = self.tree.layout.get(clip_node_id).unwrap();
66 let clip_effect = self.tree.effect_state.get(clip_node_id).unwrap();
67
68 let mut transformed_clip_area = clip_layout_node.visible_area();
69
70 for id in clip_effect.scales.iter() {
73 let scale_layout_node = self.tree.layout.get(id).unwrap();
74 let scale_effect = self.tree.effect_state.get(id).unwrap();
75 let area = scale_layout_node.visible_area();
76 let center = area.center();
77 let scale = scale_effect.scale.unwrap();
78
79 transformed_clip_area =
80 transformed_clip_area.translate(-center.to_vector());
81 transformed_clip_area =
82 transformed_clip_area.scale(scale.x, scale.y);
83 transformed_clip_area =
84 transformed_clip_area.translate(center.to_vector());
85 }
86
87 if !visible_area.intersects(&transformed_clip_area) {
89 self.canvas.restore_to_count(layer);
90 continue 'rendering;
91 }
92
93 let clip_context = ClipContext {
94 canvas: self.canvas,
95 visible_area: &transformed_clip_area,
96 scale_factor: self.scale_factor,
97 };
98
99 clip_element.clip(clip_context);
100 }
101 });
102
103 for id in effect_state.rotations.iter() {
105 let layout_node = self.tree.layout.get(id).unwrap();
106 let effect = self.tree.effect_state.get(id).unwrap();
107 let area = layout_node.visible_area();
108 let mut matrix = SkMatrix::new_identity();
109 matrix.set_rotate(
110 effect.rotation.unwrap(),
111 Some(SkPoint {
112 x: area.min_x() + area.width() / 2.0,
113 y: area.min_y() + area.height() / 2.0,
114 }),
115 );
116 self.canvas.concat(&matrix);
117 }
118
119 let render_rect = element.render_rect(&visible_area, self.scale_factor as f32);
120
121 for opacity in effect_state.opacities.iter() {
123 self.canvas
124 .save_layer_alpha_f(*render_rect.rect(), *opacity);
125 }
126
127 for id in effect_state.scales.iter() {
129 let layout_node = self.tree.layout.get(id).unwrap();
130 let effect = self.tree.effect_state.get(id).unwrap();
131 let area = layout_node.visible_area();
132 let center = area.center();
133 let scale = effect.scale.unwrap();
134
135 self.canvas.translate((center.x, center.y));
136 self.canvas.scale((scale.x, scale.y));
137 self.canvas.translate((-center.x, -center.y));
138 }
139 }
140
141 let render_context = RenderContext {
142 font_collection: self.font_collection,
143 canvas: self.canvas,
144 layout_node,
145 tree: self.tree,
146 text_style_state,
147 scale_factor: self.scale_factor,
148 };
149
150 hotpath::measure_block!("Element Render", {
151 element.render(render_context);
152 });
153
154 if let Some(effect_state) = effect_state {
155 let visible_area = layout_node.visible_area();
156 let render_rect = element.render_rect(&visible_area, self.scale_factor as f32);
157 if let Some(blur_radius) = effect_state.blur {
159 let style = element.style();
160
161 let image_filter = blur(
162 (
163 blur_radius * self.scale_factor as f32,
164 blur_radius * self.scale_factor as f32,
165 ),
166 None,
167 None,
168 render_rect.rect(),
169 );
170 if let Some(image_filter) = image_filter {
171 let rec = SaveLayerRec::default()
172 .bounds(render_rect.rect())
173 .backdrop(&image_filter);
174 if style.corner_radius.is_round() {
175 self.canvas.clip_rrect(render_rect, ClipOp::Intersect, true);
176 self.canvas.save_layer(&rec);
177 } else {
178 self.canvas.save_layer(&rec);
179 }
180 }
181 }
182 }
183
184 self.canvas.restore_to_count(layer);
185 }
186 }
187 }
188}