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;
10use i_slint_core::renderer::DrawOutcome;
11pub use i_slint_renderer_software::SoftwareRenderer;
12use i_slint_renderer_software::{PremultipliedRgbaColor, RepaintBufferType, TargetPixel};
13use std::cell::RefCell;
14use std::rc::Rc;
15use std::sync::Arc;
16use winit::event_loop::ActiveEventLoop;
17
18use super::WinitCompatibleRenderer;
19
20pub struct WinitSoftwareRenderer {
21 renderer: SoftwareRenderer,
22 _context: RefCell<Option<softbuffer::Context<Arc<winit::window::Window>>>>,
23 surface: RefCell<
24 Option<softbuffer::Surface<Arc<winit::window::Window>, Arc<winit::window::Window>>>,
25 >,
26}
27
28#[repr(transparent)]
29#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
30struct SoftBufferPixel(pub u32);
31
32impl From<SoftBufferPixel> for PremultipliedRgbaColor {
33 #[inline]
34 fn from(pixel: SoftBufferPixel) -> Self {
35 let v = pixel.0;
36 PremultipliedRgbaColor {
37 red: (v >> 16) as u8,
38 green: (v >> 8) as u8,
39 blue: v as u8,
40 alpha: (v >> 24) as u8,
41 }
42 }
43}
44
45impl From<PremultipliedRgbaColor> for SoftBufferPixel {
46 #[inline]
47 fn from(pixel: PremultipliedRgbaColor) -> Self {
48 Self(
49 (pixel.alpha as u32) << 24
50 | ((pixel.red as u32) << 16)
51 | ((pixel.green as u32) << 8)
52 | (pixel.blue as u32),
53 )
54 }
55}
56
57impl TargetPixel for SoftBufferPixel {
58 fn blend(&mut self, color: PremultipliedRgbaColor) {
59 let mut x = PremultipliedRgbaColor::from(*self);
60 x.blend(color);
61 *self = x.into();
62 }
63
64 fn from_rgb(r: u8, g: u8, b: u8) -> Self {
65 Self(0xff000000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32))
66 }
67
68 fn background() -> Self {
69 Self(0)
70 }
71}
72
73impl WinitSoftwareRenderer {
74 pub fn new_suspended(
75 _shared_backend_data: &Rc<crate::SharedBackendData>,
76 ) -> Result<Box<dyn WinitCompatibleRenderer>, PlatformError> {
77 Ok(Box::new(Self {
78 renderer: SoftwareRenderer::new(),
79 _context: RefCell::new(None),
80 surface: RefCell::new(None),
81 }))
82 }
83}
84
85impl super::WinitCompatibleRenderer for WinitSoftwareRenderer {
86 fn render(&self, window: &i_slint_core::api::Window) -> Result<DrawOutcome, PlatformError> {
87 let size = window.size();
88
89 let Some((width, height)) = size.width.try_into().ok().zip(size.height.try_into().ok())
90 else {
91 return Ok(DrawOutcome::Success);
93 };
94
95 let mut borrowed_surface = self.surface.borrow_mut();
96 let Some(surface) = borrowed_surface.as_mut() else {
97 return Ok(DrawOutcome::Success);
99 };
100
101 let winit_window = surface.window().clone();
102
103 surface
104 .resize(width, height)
105 .map_err(|e| format!("Error resizing softbuffer surface: {e}"))?;
106
107 let mut target_buffer = surface
108 .buffer_mut()
109 .map_err(|e| format!("Error retrieving softbuffer rendering buffer: {e}"))?;
110
111 let age = target_buffer.age();
112 self.renderer.set_repaint_buffer_type(match age {
113 1 => RepaintBufferType::ReusedBuffer,
114 2 => RepaintBufferType::SwappedBuffers,
115 _ => RepaintBufferType::NewBuffer,
116 });
117
118 let region = if std::env::var_os("SLINT_LINE_BY_LINE").is_none() {
119 let buffer: &mut [SoftBufferPixel] =
120 bytemuck::cast_slice_mut(target_buffer.deref_mut());
121 self.renderer.render(buffer, width.get() as usize)
122 } else {
123 struct FrameBuffer<'a> {
125 buffer: &'a mut [u32],
126 line: Vec<i_slint_renderer_software::Rgb565Pixel>,
127 }
128 impl i_slint_renderer_software::LineBufferProvider for FrameBuffer<'_> {
129 type TargetPixel = i_slint_renderer_software::Rgb565Pixel;
130 fn process_line(
131 &mut self,
132 line: usize,
133 range: core::ops::Range<usize>,
134 render_fn: impl FnOnce(&mut [Self::TargetPixel]),
135 ) {
136 let line_begin = line * self.line.len();
137 let sub = &mut self.line[..range.len()];
138 render_fn(sub);
139 for (dst, src) in self.buffer[line_begin..][range].iter_mut().zip(sub) {
140 let p = Rgb8Pixel::from(*src);
141 *dst =
142 0xff000000 | ((p.r as u32) << 16) | ((p.g as u32) << 8) | (p.b as u32);
143 }
144 }
145 }
146 self.renderer.render_by_line(FrameBuffer {
147 buffer: &mut target_buffer,
148 line: vec![Default::default(); width.get() as usize],
149 })
150 };
151
152 let size = region.bounding_box_size();
153 if let Some((w, h)) = Option::zip(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
154 {
155 winit_window.pre_present_notify();
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(DrawOutcome::Success)
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}