devotee_backend_softbuffer/
surface.rs

1use 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
57/// Render surface implementation.
58pub 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}