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() || visual.is_render_suppressed() || !(opacity > 0.0) {
41 return;
42 }
43
44 let rect = Rect::from_size(bounds.size());
45 let mut 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 if let Some(manipulation) = visual.manipulation_transform() {
55 let position = Matrix::create_translation(bounds.position().x, bounds.position().y);
56 transform = transform * (-position) * manipulation * position;
57 }
58
59 let text_options = visual.text_options();
60 let s_text_options = if text_options != TextOptions::default() {
61 context.push_text_options(text_options)
62 } else {
63 PushedState::NONE
64 };
65 let render_options = visual.render_options();
66 let s_render_options = if render_options != RenderOptions::default() {
67 context.push_render_options(render_options)
68 } else {
69 PushedState::NONE
70 };
71 let s_transform = context.push_transform(transform);
72 let s_mirror = if visual.has_mirror_transform() {
73 context.push_transform(Matrix::new(-1.0, 0.0, 0.0, 1.0, visual.bounds().width, 0.0))
74 } else {
75 PushedState::NONE
76 };
77 let s_opacity = context.push_opacity(opacity);
78 let clip_to_bounds = visual.clip_to_bounds();
79 let s_clip = if clip_to_bounds {
80 match visual.clip_to_bounds_radius() {
81 Some(radius) => context.push_rounded_clip(RoundedRect::from_corner_radius(rect, radius)),
82 None => context.push_clip(rect),
83 }
84 } else {
85 PushedState::NONE
86 };
87 let s_geometry_clip = match visual.clip() {
88 Some(clip) => context.push_geometry_clip(&clip),
89 None => PushedState::NONE,
90 };
91 let s_opacity_mask = match visual.opacity_mask() {
92 Some(mask) => context.push_opacity_mask(&mask, rect),
93 None => PushedState::NONE,
94 };
95 let effect = visual.effect();
96 let s_effect = match &effect {
97 Some(effect) => context.push_effect(effect, rect),
98 None => PushedState::NONE,
99 };
100
101 {
102 let mut total_transform = transform * parent_transform;
103 let effect_rect = match &effect {
104 Some(effect) => rect.inflate_thickness(crate::media::EffectExtensions::get_effect_output_padding(Some(&**effect))),
105 None => rect,
106 };
107 let visual_bounds = effect_rect.transform_to_aabb(total_transform);
108
109 if visual_bounds.intersects(clip_rect) {
110 visual.render(context);
111 }
112
113 if clip_to_bounds {
114 total_transform = Matrix::IDENTITY;
115 clip_rect = rect;
116 }
117
118 if let Some(children) = visual.visual_children_snapshot() {
119 if visual.has_non_uniform_z_index_children() {
120 let mut sorted: Vec<Ref<Visual>> = children.iter().cloned().collect();
121 sorted.sort_by(ZIndexComparer::compare);
123 for child in &sorted {
124 Self::render_core(context, child, child.bounds(), total_transform, clip_rect);
125 }
126 } else {
127 for child in children.iter() {
128 Self::render_core(context, child, child.bounds(), total_transform, clip_rect);
129 }
130 }
131 }
132 }
133
134 context.pop(s_effect);
135 context.pop(s_opacity_mask);
136 context.pop(s_geometry_clip);
137 context.pop(s_clip);
138 context.pop(s_opacity);
139 context.pop(s_mirror);
140 context.pop(s_transform);
141 context.pop(s_render_options);
142 context.pop(s_text_options);
143 }
144}