devotee_backend_softbuffer/
surface.rs1use std::num::NonZeroU32;
2use std::ops::{Deref, DerefMut};
3use std::rc::Rc;
4
5use devotee_backend::middling::{
6 Fill, 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_size = PhysicalSize::new(
75 render_window_size.width.min(surface_size.width),
76 render_window_size.height.min(surface_size.height),
77 );
78 let render_window = (render_window_position, render_window_size);
79 Self {
80 internal,
81 surface_size,
82 scale,
83 render_window,
84 }
85 }
86
87 pub(super) fn clear(&mut self, color: u32) -> Result<(), softbuffer::SoftBufferError> {
88 self.internal.fill(color);
89 Ok(())
90 }
91
92 pub(super) fn present(self) -> Result<(), softbuffer::SoftBufferError> {
93 self.internal.present()
94 }
95}
96
97impl Fill for SoftSurface<'_> {
98 fn fill_from(&mut self, data: &[Self::Texel]) {
99 let start_x = self.render_window.0.x;
100 let start_y = self.render_window.0.y;
101
102 for y in 0..self.render_window.1.height {
103 for x in 0..self.render_window.1.width {
104 if let Some(pixel) = data.get((x + y * self.render_window.1.width) as usize) {
105 for internal_y in 0..self.scale {
106 let index = ((start_x + x * self.scale) as usize)
107 + ((start_y + internal_y + (y * self.scale)) * self.surface_size.width)
108 as usize;
109 if let Some(buffer) =
110 self.internal.get_mut(index..(index + self.scale as usize))
111 {
112 buffer.fill(*pixel);
113 }
114 }
115 }
116 }
117 }
118 }
119}
120
121impl TexelDesignatorRef<'_> for SoftSurface<'_> {
122 type TexelRef = TexelReader;
123}
124
125impl<'t> TexelDesignatorMut<'t> for SoftSurface<'_> {
126 type TexelMut = TexelWriter<'t>;
127}
128
129impl Surface for SoftSurface<'_> {
130 type Texel = u32;
131
132 fn texel(&self, x: u32, y: u32) -> Option<TexelRef<'_, Self>> {
133 let (x, y) = filter_coords(x, y, self.render_window)?;
134 TexelReader::try_new(
135 x,
136 y,
137 self.render_window.0,
138 self.surface_size,
139 self.scale,
140 self.internal.deref(),
141 )
142 }
143
144 fn texel_mut(&mut self, x: u32, y: u32) -> Option<TexelMut<'_, Self>> {
145 let (x, y) = filter_coords(x, y, self.render_window)?;
146 TexelWriter::try_new(
147 x,
148 y,
149 self.render_window.0,
150 self.surface_size,
151 self.scale,
152 self.internal.deref_mut(),
153 )
154 }
155
156 unsafe fn unsafe_texel(&self, x: u32, y: u32) -> TexelRef<'_, Self> {
157 TexelReader::new(
158 x,
159 y,
160 self.render_window.0,
161 self.surface_size,
162 self.scale,
163 self.internal.deref(),
164 )
165 }
166
167 unsafe fn unsafe_texel_mut(&mut self, x: u32, y: u32) -> TexelMut<'_, Self> {
168 TexelWriter::new(
169 x,
170 y,
171 self.render_window.0,
172 self.surface_size,
173 self.scale,
174 self.internal.deref_mut(),
175 )
176 }
177
178 fn clear(&mut self, value: Self::Texel) {
179 for y in self.render_window.0.y
180 ..(self.render_window.0.y + self.render_window.1.height * self.scale)
181 {
182 if let Some(slice) = self.internal.get_mut(
183 ((self.render_window.0.x + y * self.surface_size.width) as usize)
184 ..((self.render_window.0.x
185 + self.render_window.1.width * self.scale
186 + y * self.surface_size.width) as usize),
187 ) {
188 slice.fill(value);
189 }
190 }
191 }
192
193 fn width(&self) -> u32 {
194 self.render_window.1.width
195 }
196
197 fn height(&self) -> u32 {
198 self.render_window.1.height
199 }
200}
201
202pub struct TexelReader {
203 cache: u32,
204}
205
206impl TexelReader {
207 fn new(
208 x: u32,
209 y: u32,
210 window_start: PhysicalPosition<u32>,
211 surface_size: PhysicalSize<u32>,
212 scale: u32,
213 buffer: &[u32],
214 ) -> Self {
215 let cache =
216 buffer[(window_start.x + x * scale + (y * scale + window_start.y) * surface_size.width)
217 as usize];
218 Self { cache }
219 }
220
221 fn try_new(
222 x: u32,
223 y: u32,
224 window_start: PhysicalPosition<u32>,
225 surface_size: PhysicalSize<u32>,
226 scale: u32,
227 buffer: &[u32],
228 ) -> Option<Self> {
229 {
230 let (x, y) = (window_start.x + x * scale, window_start.y + y * scale);
231 if x >= surface_size.width || y >= surface_size.height {
232 return None;
233 }
234 }
235 let cache = *buffer.get(
236 (window_start.x + x * scale + (y * scale + window_start.y) * surface_size.width)
237 as usize,
238 )?;
239 Some(Self { cache })
240 }
241}
242
243impl Deref for TexelReader {
244 type Target = u32;
245
246 fn deref(&self) -> &Self::Target {
247 &self.cache
248 }
249}
250
251pub struct TexelWriter<'a> {
252 start_x: u32,
253 start_y: u32,
254 surface_size: PhysicalSize<u32>,
255 scale: u32,
256 buffer: &'a mut [u32],
257 cache: u32,
258}
259
260impl<'a> TexelWriter<'a> {
261 fn new(
262 x: u32,
263 y: u32,
264 window_start: PhysicalPosition<u32>,
265 surface_size: PhysicalSize<u32>,
266 scale: u32,
267 buffer: &'a mut [u32],
268 ) -> Self {
269 let start_x = window_start.x + x * scale;
270 let start_y = window_start.y + y * scale;
271 let cache = buffer[(start_x + start_y * surface_size.width) as usize];
272
273 Self {
274 start_x,
275 start_y,
276 surface_size,
277 scale,
278 buffer,
279 cache,
280 }
281 }
282
283 fn try_new(
284 x: u32,
285 y: u32,
286 window_start: PhysicalPosition<u32>,
287 surface_size: PhysicalSize<u32>,
288 scale: u32,
289 buffer: &'a mut [u32],
290 ) -> Option<Self> {
291 let start_x = window_start.x + x * scale;
292 let start_y = window_start.y + y * scale;
293 {
294 let (x, y) = (start_x, start_y);
295 if x >= surface_size.width || y >= surface_size.height {
296 return None;
297 }
298 }
299 let cache = *buffer.get((start_x + start_y * surface_size.width) as usize)?;
300
301 Some(Self {
302 start_x,
303 start_y,
304 surface_size,
305 scale,
306 buffer,
307 cache,
308 })
309 }
310}
311
312impl Deref for TexelWriter<'_> {
313 type Target = u32;
314
315 fn deref(&self) -> &Self::Target {
316 &self.cache
317 }
318}
319
320impl DerefMut for TexelWriter<'_> {
321 fn deref_mut(&mut self) -> &mut Self::Target {
322 &mut self.cache
323 }
324}
325
326impl Drop for TexelWriter<'_> {
327 fn drop(&mut self) {
328 let start_x = self.start_x;
329 let end_x = (start_x + self.scale).min(self.surface_size.width);
330 for y in self.start_y..(self.start_y + self.scale) {
331 let slice_y = y * self.surface_size.width;
332 self.buffer[((start_x + slice_y) as usize)..((end_x + slice_y) as usize)]
333 .fill(self.cache)
334 }
335 }
336}