ferroui_base/rendering/
immediate_renderer.rs1use super::ZIndexComparer;
2use crate::media::{DrawingContext, PushedState, RenderOptions, TextOptions};
3use crate::{Matrix, Rect, Ref, RoundedRect, Visual};
4
5pub struct ImmediateRenderer;
8
9impl ImmediateRenderer {
10 pub fn render(context: &mut DrawingContext<'_>, visual: &Ref<Visual>) {
12 Self::render_clipped(context, visual, Rect::from_size(visual.bounds().size()));
13 }
14
15 pub fn render_clipped(context: &mut DrawingContext<'_>, visual: &Ref<Visual>, clip_rect: Rect) {
17 let transform =
18 context.push_transform(Matrix::create_translation(-clip_rect.position().x, -clip_rect.position().y));
19 let clip = context.push_clip(clip_rect);
20 Self::render_core(
21 context,
22 visual,
23 Rect::from_size(visual.bounds().size()),
24 Matrix::IDENTITY,
25 Rect::from_size(clip_rect.size()),
26 );
27 context.pop(clip);
28 context.pop(transform);
29 }
30
31 fn render_core(
32 context: &mut DrawingContext<'_>,
33 visual: &Ref<Visual>,
34 bounds: Rect,
35 parent_transform: Matrix,
36 mut clip_rect: Rect,
37 ) {
38 let opacity = visual.opacity();
39 if !visual.is_visible() || !(opacity > 0.0) {
41 return;
42 }
43
44 let rect = Rect::from_size(bounds.size());
45 let transform = match visual.render_transform().map(|t| t.value()) {
46 Some(rt) => {
47 let origin = visual.render_transform_origin().to_pixels(visual.bounds().size());
48 let offset = Matrix::create_translation(origin.x, origin.y);
49 (-offset) * rt * offset * Matrix::create_translation(bounds.position().x, bounds.position().y)
50 }
51 None => Matrix::create_translation(bounds.position().x, bounds.position().y),
52 };
53
54 let text_options = visual.text_options();
55 let s_text_options = if text_options != TextOptions::default() {
56 context.push_text_options(text_options)
57 } else {
58 PushedState::NONE
59 };
60 let render_options = visual.render_options();
61 let s_render_options = if render_options != RenderOptions::default() {
62 context.push_render_options(render_options)
63 } else {
64 PushedState::NONE
65 };
66 let s_transform = context.push_transform(transform);
67 let s_mirror = if visual.has_mirror_transform() {
68 context.push_transform(Matrix::new(-1.0, 0.0, 0.0, 1.0, visual.bounds().width, 0.0))
69 } else {
70 PushedState::NONE
71 };
72 let s_opacity = context.push_opacity(opacity);
73 let clip_to_bounds = visual.clip_to_bounds();
74 let s_clip = if clip_to_bounds {
75 match visual.clip_to_bounds_radius() {
76 Some(radius) => context.push_rounded_clip(RoundedRect::from_corner_radius(rect, radius)),
77 None => context.push_clip(rect),
78 }
79 } else {
80 PushedState::NONE
81 };
82 let s_geometry_clip = match visual.clip() {
83 Some(clip) => context.push_geometry_clip(&clip),
84 None => PushedState::NONE,
85 };
86 let s_opacity_mask = match visual.opacity_mask() {
87 Some(mask) => context.push_opacity_mask(&mask, rect),
88 None => PushedState::NONE,
89 };
90 let effect = visual.effect();
91 let s_effect = match &effect {
92 Some(effect) => context.push_effect(effect, rect),
93 None => PushedState::NONE,
94 };
95
96 {
97 let mut total_transform = transform * parent_transform;
98 let effect_rect = match &effect {
99 Some(effect) => rect.inflate_thickness(crate::media::EffectExtensions::get_effect_output_padding(Some(&**effect))),
100 None => rect,
101 };
102 let visual_bounds = effect_rect.transform_to_aabb(total_transform);
103
104 if visual_bounds.intersects(clip_rect) {
105 visual.render(context);
106 }
107
108 if clip_to_bounds {
109 total_transform = Matrix::IDENTITY;
110 clip_rect = rect;
111 }
112
113 if let Some(children) = visual.visual_children_snapshot() {
114 if visual.has_non_uniform_z_index_children() {
115 let mut sorted: Vec<Ref<Visual>> = children.iter().cloned().collect();
116 sorted.sort_by(ZIndexComparer::compare);
118 for child in &sorted {
119 Self::render_core(context, child, child.bounds(), total_transform, clip_rect);
120 }
121 } else {
122 for child in children.iter() {
123 Self::render_core(context, child, child.bounds(), total_transform, clip_rect);
124 }
125 }
126 }
127 }
128
129 context.pop(s_effect);
130 context.pop(s_opacity_mask);
131 context.pop(s_geometry_clip);
132 context.pop(s_clip);
133 context.pop(s_opacity);
134 context.pop(s_mirror);
135 context.pop(s_transform);
136 context.pop(s_render_options);
137 context.pop(s_text_options);
138 }
139}