i_slint_backend_winit/renderer/
sw.rs1use core::num::NonZeroU32;
7use core::ops::DerefMut;
8use i_slint_core::graphics::Rgb8Pixel;
9use i_slint_core::platform::PlatformError;
10pub use i_slint_core::software_renderer::SoftwareRenderer;
11use i_slint_core::software_renderer::{PremultipliedRgbaColor, RepaintBufferType, TargetPixel};
12use std::cell::RefCell;
13use std::rc::Rc;
14use std::sync::Arc;
15use winit::event_loop::ActiveEventLoop;
16
17use super::WinitCompatibleRenderer;
18
19pub struct WinitSoftwareRenderer {
20 renderer: SoftwareRenderer,
21 _context: RefCell<Option<softbuffer::Context<Arc<winit::window::Window>>>>,
22 surface: RefCell<
23 Option<softbuffer::Surface<Arc<winit::window::Window>, Arc<winit::window::Window>>>,
24 >,
25}
26
27#[repr(transparent)]
28#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
29struct SoftBufferPixel(pub u32);
30
31impl From<SoftBufferPixel> for PremultipliedRgbaColor {
32 #[inline]
33 fn from(pixel: SoftBufferPixel) -> Self {
34 let v = pixel.0;
35 PremultipliedRgbaColor {
36 red: (v >> 16) as u8,
37 green: (v >> 8) as u8,
38 blue: (v >> 0) as u8,
39 alpha: (v >> 24) as u8,
40 }
41 }
42}
43
44impl From<PremultipliedRgbaColor> for SoftBufferPixel {
45 #[inline]
46 fn from(pixel: PremultipliedRgbaColor) -> Self {
47 Self(
48 (pixel.alpha as u32) << 24
49 | ((pixel.red as u32) << 16)
50 | ((pixel.green as u32) << 8)
51 | (pixel.blue as u32),
52 )
53 }
54}
55
56impl TargetPixel for SoftBufferPixel {
57 fn blend(&mut self, color: PremultipliedRgbaColor) {
58 let mut x = PremultipliedRgbaColor::from(*self);
59 x.blend(color);
60 *self = x.into();
61 }
62
63 fn from_rgb(r: u8, g: u8, b: u8) -> Self {
64 Self(0xff000000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32))
65 }
66
67 fn background() -> Self {
68 Self(0)
69 }
70}
71
72impl WinitSoftwareRenderer {
73 pub fn new_suspended(
74 _shared_backend_data: &Rc<crate::SharedBackendData>,
75 ) -> Result<Box<dyn WinitCompatibleRenderer>, PlatformError> {
76 Ok(Box::new(Self {
77 renderer: SoftwareRenderer::new(),
78 _context: RefCell::new(None),
79 surface: RefCell::new(None),
80 }))
81 }
82}
83
84impl super::WinitCompatibleRenderer for WinitSoftwareRenderer {
85 fn render(&self, window: &i_slint_core::api::Window) -> Result<(), PlatformError> {
86 let size = window.size();
87
88 let Some((width, height)) = size.width.try_into().ok().zip(size.height.try_into().ok())
89 else {
90 return Ok(());
92 };
93
94 let mut borrowed_surface = self.surface.borrow_mut();
95 let Some(surface) = borrowed_surface.as_mut() else {
96 return Ok(());
98 };
99
100 let winit_window = surface.window().clone();
101
102 surface
103 .resize(width, height)
104 .map_err(|e| format!("Error resizing softbuffer surface: {e}"))?;
105
106 let mut target_buffer = surface
107 .buffer_mut()
108 .map_err(|e| format!("Error retrieving softbuffer rendering buffer: {e}"))?;
109
110 let age = target_buffer.age();
111 self.renderer.set_repaint_buffer_type(match age {
112 1 => RepaintBufferType::ReusedBuffer,
113 2 => RepaintBufferType::SwappedBuffers,
114 _ => RepaintBufferType::NewBuffer,
115 });
116
117 let region = if std::env::var_os("SLINT_LINE_BY_LINE").is_none() {
118 let buffer: &mut [SoftBufferPixel] =
119 bytemuck::cast_slice_mut(target_buffer.deref_mut());
120 self.renderer.render(buffer, width.get() as usize)
121 } else {
122 struct FrameBuffer<'a> {
124 buffer: &'a mut [u32],
125 line: Vec<i_slint_core::software_renderer::Rgb565Pixel>,
126 }
127 impl i_slint_core::software_renderer::LineBufferProvider for FrameBuffer<'_> {
128 type TargetPixel = i_slint_core::software_renderer::Rgb565Pixel;
129 fn process_line(
130 &mut self,
131 line: usize,
132 range: core::ops::Range<usize>,
133 render_fn: impl FnOnce(&mut [Self::TargetPixel]),
134 ) {
135 let line_begin = line * self.line.len();
136 let sub = &mut self.line[..range.len()];
137 render_fn(sub);
138 for (dst, src) in self.buffer[line_begin..][range].iter_mut().zip(sub) {
139 let p = Rgb8Pixel::from(*src);
140 *dst =
141 0xff000000 | ((p.r as u32) << 16) | ((p.g as u32) << 8) | (p.b as u32);
142 }
143 }
144 }
145 self.renderer.render_by_line(FrameBuffer {
146 buffer: &mut target_buffer,
147 line: vec![Default::default(); width.get() as usize],
148 })
149 };
150
151 winit_window.pre_present_notify();
152
153 let size = region.bounding_box_size();
154 if let Some((w, h)) = Option::zip(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
155 {
156 let pos = region.bounding_box_origin();
157 target_buffer
158 .present_with_damage(&[softbuffer::Rect {
159 width: w,
160 height: h,
161 x: pos.x as u32,
162 y: pos.y as u32,
163 }])
164 .map_err(|e| format!("Error presenting softbuffer buffer: {e}"))?;
165 }
166 Ok(())
167 }
168
169 fn as_core_renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
170 &self.renderer
171 }
172
173 fn occluded(&self, _: bool) {
174 self.renderer.set_repaint_buffer_type(RepaintBufferType::NewBuffer);
177 }
178
179 fn resume(
180 &self,
181 active_event_loop: &ActiveEventLoop,
182 window_attributes: winit::window::WindowAttributes,
183 ) -> Result<Arc<winit::window::Window>, PlatformError> {
184 let winit_window =
185 active_event_loop.create_window(window_attributes).map_err(|winit_os_error| {
186 PlatformError::from(format!(
187 "Error creating native window for software rendering: {winit_os_error}"
188 ))
189 })?;
190 let winit_window = Arc::new(winit_window);
191
192 let context = softbuffer::Context::new(winit_window.clone())
193 .map_err(|e| format!("Error creating softbuffer context: {e}"))?;
194
195 let surface = softbuffer::Surface::new(&context, winit_window.clone()).map_err(
196 |softbuffer_error| format!("Error creating softbuffer surface: {softbuffer_error}"),
197 )?;
198
199 *self._context.borrow_mut() = Some(context);
200 *self.surface.borrow_mut() = Some(surface);
201
202 Ok(winit_window)
203 }
204
205 fn suspend(&self) -> Result<(), PlatformError> {
206 drop(self.surface.borrow_mut().take());
207 drop(self._context.borrow_mut().take());
208 Ok(())
209 }
210}