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 style::shadow::ShadowPosition,
19 tree::Tree,
20};
21
22pub struct RenderPipeline<'a> {
23 pub font_collection: &'a mut FontCollection,
24 pub font_manager: &'a FontMgr,
25 pub canvas: &'a Canvas,
26 pub tree: &'a Tree,
27 pub scale_factor: f64,
28 pub background: Color,
29}
30
31impl RenderPipeline<'_> {
32 #[cfg_attr(feature = "hotpath", hotpath::measure)]
33 pub fn render(self) {
34 self.canvas.clear(self.background);
35
36 for i16 in itertools::sorted(self.tree.layers.keys()) {
38 let nodes = self.tree.layers.get(i16).unwrap();
39 'rendering: for node_id in nodes {
40 let layout_node = self.tree.layout.get(node_id).unwrap();
41
42 if layout_node.hidden {
43 continue 'rendering;
44 }
45
46 let layer = self.canvas.save();
47
48 let element = self.tree.elements.get(node_id).unwrap();
49 let text_style_state = self.tree.text_style_state.get(node_id).unwrap();
50 let effect_state = self.tree.effect_state.get(node_id);
51
52 if let Some(effect_state) = effect_state {
53 let mut visible_area = layout_node.visible_area();
54
55 for id in effect_state.scales.iter() {
57 let layout_node = self.tree.layout.get(id).unwrap();
58 let effect = self.tree.effect_state.get(id).unwrap();
59 let area = layout_node.visible_area();
60 let origin = effect.transform_origin.origin(&area);
61 let scale = effect.scale.unwrap();
62
63 visible_area = visible_area.translate(-origin.to_vector());
64 visible_area = visible_area.scale(scale.x, scale.y);
65 visible_area = visible_area.translate(origin.to_vector());
66 }
67
68 hotpath::measure_block!("Element Clipping", {
69 for clip_node_id in effect_state.clips.iter() {
70 let clip_element = self.tree.elements.get(clip_node_id).unwrap();
71 let clip_layout_node = self.tree.layout.get(clip_node_id).unwrap();
72 let clip_effect = self.tree.effect_state.get(clip_node_id).unwrap();
73
74 let mut transformed_clip_area = clip_layout_node.visible_area();
75
76 for id in clip_effect.scales.iter() {
79 let scale_layout_node = self.tree.layout.get(id).unwrap();
80 let scale_effect = self.tree.effect_state.get(id).unwrap();
81 let area = scale_layout_node.visible_area();
82 let origin = scale_effect.transform_origin.origin(&area);
83 let scale = scale_effect.scale.unwrap();
84
85 transformed_clip_area =
86 transformed_clip_area.translate(-origin.to_vector());
87 transformed_clip_area =
88 transformed_clip_area.scale(scale.x, scale.y);
89 transformed_clip_area =
90 transformed_clip_area.translate(origin.to_vector());
91 }
92
93 if !visible_area.intersects(&transformed_clip_area) {
95 self.canvas.restore_to_count(layer);
96 continue 'rendering;
97 }
98
99 let clip_context = ClipContext {
100 canvas: self.canvas,
101 visible_area: &transformed_clip_area,
102 scale_factor: self.scale_factor,
103 };
104
105 clip_element.clip(clip_context);
106 }
107 });
108
109 for id in effect_state.rotations.iter() {
111 let layout_node = self.tree.layout.get(id).unwrap();
112 let effect = self.tree.effect_state.get(id).unwrap();
113 let area = layout_node.visible_area();
114 let origin = effect.transform_origin.origin(&area);
115 let mut matrix = SkMatrix::new_identity();
116 matrix.set_rotate(
117 effect.rotation.unwrap(),
118 Some(SkPoint {
119 x: origin.x,
120 y: origin.y,
121 }),
122 );
123 self.canvas.concat(&matrix);
124 }
125
126 let render_rect = element.render_rect(&visible_area, self.scale_factor as f32);
127
128 let mut layer_bounds = *render_rect.rect();
131 let scale_factor = self.scale_factor as f32;
132
133 for shadow in element.style().shadows.iter() {
134 if shadow.position == ShadowPosition::Normal {
135 let outset_x = shadow.x.abs() + shadow.spread + shadow.blur;
136 let outset_y = shadow.y.abs() + shadow.spread + shadow.blur;
137 layer_bounds = layer_bounds
138 .with_outset((outset_x * scale_factor, outset_y * scale_factor));
139 }
140 }
141
142 for opacity in effect_state.opacities.iter() {
143 self.canvas.save_layer_alpha_f(layer_bounds, *opacity);
144 }
145
146 for id in effect_state.scales.iter() {
148 let layout_node = self.tree.layout.get(id).unwrap();
149 let effect = self.tree.effect_state.get(id).unwrap();
150 let area = layout_node.visible_area();
151 let origin = effect.transform_origin.origin(&area);
152 let scale = effect.scale.unwrap();
153
154 self.canvas.translate((origin.x, origin.y));
155 self.canvas.scale((scale.x, scale.y));
156 self.canvas.translate((-origin.x, -origin.y));
157 }
158 }
159
160 let render_context = RenderContext {
161 font_collection: self.font_collection,
162 canvas: self.canvas,
163 layout_node,
164 tree: self.tree,
165 text_style_state,
166 scale_factor: self.scale_factor,
167 };
168
169 hotpath::measure_block!("Element Render", {
170 element.render(render_context);
171 });
172
173 if let Some(effect_state) = effect_state {
174 let visible_area = layout_node.visible_area();
175 let render_rect = element.render_rect(&visible_area, self.scale_factor as f32);
176 if let Some(blur_radius) = effect_state.blur {
178 let style = element.style();
179
180 let image_filter = blur(
181 (
182 blur_radius * self.scale_factor as f32,
183 blur_radius * self.scale_factor as f32,
184 ),
185 None,
186 None,
187 render_rect.rect(),
188 );
189 if let Some(image_filter) = image_filter {
190 let rec = SaveLayerRec::default()
191 .bounds(render_rect.rect())
192 .backdrop(&image_filter);
193 if style.corner_radius.is_round() {
194 self.canvas.clip_rrect(render_rect, ClipOp::Intersect, true);
195 self.canvas.save_layer(&rec);
196 } else {
197 self.canvas.save_layer(&rec);
198 }
199 }
200 }
201 }
202
203 self.canvas.restore_to_count(layer);
204 }
205 }
206 }
207}