devotee_backend_softbuffer/
surface.rs1use std::num::NonZeroU32;
2use std::ops::{Deref, DerefMut};
3use std::rc::Rc;
4
5use devotee_backend::middling::{
6 Surface, TexelDesignatorMut, TexelDesignatorRef, TexelMut, TexelRef,
7};
8use winit::dpi::{PhysicalPosition, PhysicalSize};
9use winit::window::Window;
10
11pub(crate) fn estimate_render_window_position_scale(
12 surface_size: PhysicalSize<u32>,
13 scale: ScaleMode,
14 render_window_size: PhysicalSize<u32>,
15) -> (PhysicalPosition<u32>, u32) {
16 let scale = match scale {
17 ScaleMode::Fixed(scale) => scale.get(),
18 ScaleMode::Auto => (surface_size.width / render_window_size.width)
19 .min(surface_size.height / render_window_size.height)
20 .max(1),
21 };
22
23 (
24 PhysicalPosition::new(
25 surface_size
26 .width
27 .saturating_sub(render_window_size.width * scale)
28 / 2,
29 surface_size
30 .height
31 .saturating_sub(render_window_size.height * scale)
32 / 2,
33 ),
34 scale,
35 )
36}
37
38fn filter_coords(
39 x: u32,
40 y: u32,
41 window: (PhysicalPosition<u32>, PhysicalSize<u32>),
42) -> Option<(u32, u32)> {
43 if x >= window.1.width || y >= window.1.height {
44 None
45 } else {
46 Some((x, y))
47 }
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
51pub(super) enum ScaleMode {
52 #[default]
53 Auto,
54 Fixed(NonZeroU32),
55}
56
57pub struct SoftSurface<'a> {
59 internal: softbuffer::Buffer<'a, Rc<Window>, Rc<Window>>,
60 surface_size: PhysicalSize<u32>,
61 scale: u32,
62 render_window: (PhysicalPosition<u32>, PhysicalSize<u32>),
63}
64
65impl<'a> SoftSurface<'a> {
66 pub(super) fn new(
67 internal: softbuffer::Buffer<'a, Rc<Window>, Rc<Window>>,
68 surface_size: PhysicalSize<u32>,
69 scale: ScaleMode,
70 render_window_size: PhysicalSize<u32>,
71 ) -> Self {
72 let (render_window_position, scale) =
73 estimate_render_window_position_scale(surface_size, scale, render_window_size);
74 let render_window = (render_window_position, render_window_size);
75 Self {
76 internal,
77 surface_size,
78 scale,
79 render_window,
80 }
81 }
82
83 pub(super) fn clear(&mut self, color: u32) -> Result<(), softbuffer::SoftBufferError> {
84 self.internal.fill(color);
85 Ok(())
86 }
87
88 pub(super) fn present(self) -> Result<(), softbuffer::SoftBufferError> {
89 self.internal.present()
90 }
91}
92
93impl TexelDesignatorRef<'_> for SoftSurface<'_> {
94 type TexelRef = TexelReader;
95}
96
97impl<'t> TexelDesignatorMut<'t> for SoftSurface<'_> {
98 type TexelMut = TexelWriter<'t>;
99}
100
101impl Surface for SoftSurface<'_> {
102 type Texel = u32;
103
104 fn texel(&self, x: u32, y: u32) -> Option<TexelRef<'_, Self>> {
105 let (x, y) = filter_coords(x, y, self.render_window)?;
106 TexelReader::try_new(
107 x,
108 y,
109 self.render_window.0,
110 self.surface_size,
111 self.scale,
112 self.internal.deref(),
113 )
114 }
115
116 fn texel_mut(&mut self, x: u32, y: u32) -> Option<TexelMut<'_, Self>> {
117 let (x, y) = filter_coords(x, y, self.render_window)?;
118 TexelWriter::try_new(
119 x,
120 y,
121 self.render_window.0,
122 self.surface_size,
123 self.scale,
124 self.internal.deref_mut(),
125 )
126 }
127
128 fn clear(&mut self, value: Self::Texel) {
129 for y in self.render_window.0.y
130 ..(self.render_window.0.y + self.render_window.1.height * self.scale)
131 {
132 if let Some(slice) = self.internal.get_mut(
133 ((self.render_window.0.x + y * self.surface_size.width) as usize)
134 ..((self.render_window.0.x
135 + self.render_window.1.width * self.scale
136 + y * self.surface_size.width) as usize),
137 ) {
138 slice.fill(value);
139 }
140 }
141 }
142
143 fn width(&self) -> u32 {
144 self.render_window.1.width
145 }
146
147 fn height(&self) -> u32 {
148 self.render_window.1.height
149 }
150}
151
152pub struct TexelReader {
153 cache: u32,
154}
155
156impl TexelReader {
157 fn try_new(
158 x: u32,
159 y: u32,
160 window_start: PhysicalPosition<u32>,
161 surface_size: PhysicalSize<u32>,
162 scale: u32,
163 buffer: &[u32],
164 ) -> Option<Self> {
165 {
166 let (x, y) = (window_start.x + x * scale, window_start.y + y * scale);
167 if x >= surface_size.width || y >= surface_size.height {
168 return None;
169 }
170 }
171 let cache = *buffer.get(
172 (window_start.x + x * scale + (y * scale + window_start.y) * surface_size.width)
173 as usize,
174 )?;
175 Some(Self { cache })
176 }
177}
178
179impl Deref for TexelReader {
180 type Target = u32;
181
182 fn deref(&self) -> &Self::Target {
183 &self.cache
184 }
185}
186
187pub struct TexelWriter<'a> {
188 x: u32,
189 y: u32,
190 window_start: PhysicalPosition<u32>,
191 surface_size: PhysicalSize<u32>,
192 scale: u32,
193 buffer: &'a mut [u32],
194 cache: u32,
195}
196
197impl<'a> TexelWriter<'a> {
198 fn try_new(
199 x: u32,
200 y: u32,
201 window_start: PhysicalPosition<u32>,
202 surface_size: PhysicalSize<u32>,
203 scale: u32,
204 buffer: &'a mut [u32],
205 ) -> Option<Self> {
206 {
207 let (x, y) = (window_start.x + x * scale, window_start.y + y * scale);
208 if x >= surface_size.width || y >= surface_size.height {
209 return None;
210 }
211 }
212 let cache = *buffer.get(
213 (window_start.x + x * scale + (y * scale + window_start.y) * surface_size.width)
214 as usize,
215 )?;
216
217 Some(Self {
218 x,
219 y,
220 window_start,
221 surface_size,
222 scale,
223 buffer,
224 cache,
225 })
226 }
227}
228
229impl Deref for TexelWriter<'_> {
230 type Target = u32;
231
232 fn deref(&self) -> &Self::Target {
233 &self.cache
234 }
235}
236
237impl DerefMut for TexelWriter<'_> {
238 fn deref_mut(&mut self) -> &mut Self::Target {
239 &mut self.cache
240 }
241}
242
243impl Drop for TexelWriter<'_> {
244 fn drop(&mut self) {
245 let start_x = self.window_start.x + self.x * self.scale;
246 let end_x = (self.window_start.x + (self.x + 1) * self.scale).min(self.surface_size.width);
247 for y in (self.y * self.scale + self.window_start.y)
248 ..((self.y + 1) * self.scale + self.window_start.y)
249 {
250 let slice_y = y * self.surface_size.width;
251 self.buffer[((start_x + slice_y) as usize)..((end_x + slice_y) as usize)]
252 .fill(self.cache)
253 }
254 }
255}